Re: [spice v2] streaming: Always delegate bit rate control to the video encoder

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Here are some pointers on ways to test this patch.


1. Tweaking the available bandwith at will
-------------------------------------------

The point of the bit rate control algorithm is to adapt the video 
stream's bitrate to the available network bandwidth. So you could test 
how it reacts in various conditions by moving closer or further from 
your wireless access point, switching to an ethernet connection, or 
setting up port fowarding to send the stream around hosts on the 
internet. But to get reproducible conditions the best is to use tc.

Here is an example of the commands I use:
  To limit the Spice server->client traffic and not the client->server one:
  tc qdisc add dev lo handle 1:0 root htb direct_qlen 10
  tc class add dev lo parent 1:0 classid 1:11 htb rate 6000kbit #direct_qlen 10
  # Optionally increase latency. Note that a high latency will limit bandwidth.
  # With 150ms we can still achieve >> 20Mbps.
  tc qdisc add dev lo parent 1:11 netem delay 150ms 10ms
  tc filter add dev lo protocol ip prio 1 u32 match ip sport 5903 0xffff flowid 1:11
  # Prevent too much packet queueing, otherwise packets could arrive seconds
  # after being sent which is not realistic and broken (see Bufferbloat).
  ifconfig lo txqueuelen 1

The above assumes the server listens on port 5903 on localhost. It 
limits the server -> client bandwidth to 6 Mbps with a 150 ms RTT (150 
ms in one direction, essentially 0 ms in the other) with 10ms of jitter.

Initially the client will totally mis-estimate the available bandwidth, 
sometimes coming up with > 20 Gbps. So it may take a dozen seconds for 
the server to drop the bitrate low enough to get a working stream.

Then you can drop the available bitrate to see how it reacts:

  tc class change dev lo parent 1:0 classid 1:11 htb rate 4000kbit

Finally you can check the configuration and whether packets are treated as expected:
  tc -s qdisc ls dev lo
  tc -s class show dev lo
  tc -s filter show dev lo

And clear everything once you've done all your tests.
  tc qdisc del dev lo root

For more details see:
  http://www.insightfullogic.com/blog/2013/jul/25/performance-tests-slow-networks-tc/
  http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm



2. Testing the current "server-drops-only" bit rate control
-----------------------------------------------------------

It's a good idea to play with the above commands to get an idea of how 
the baseline behaves before modifying the code. If you've done so, 
you've tested the bitrate control of either the MJPEG or the GStreamer 
video encoder.

But what we want to test here is the bitrate control implemented in 
stream.c and dcc-send.c. But it is only used when two conditions are 
met:

 * dcc_use_video_encoder_rate_control() must return false, which means 
   the client cannot send stream report messages (to summarize these 
   tell the server how much margin there is at the client between when a 
   frame is received and when it must be displayed; the bigger the 
   frame, the longer it spends in transit and the shorter that margin). 
   This is essnetially for compatibility with old clients (like the 
   defunct spicec).

 * dcc_is_low_bandwidth() must return true, which means the initial
   bandwidth estimate was under 10 Mbps. This can be a problem for the 
   tests as, at least when using tc, the initial bandwidth estimate can 
   be wildly off (like >> 20Gbps when the bandwidth is capped at 6Mbps).


This gives us three cases:
 * use_video_encoder_rate_control == true
   -> Then it is the video encoder that adjusts the stream's bitrate.

 * use_video_encoder_rate_control == false && 
   is_low_bandwidth == true
   -> Then stream.c & dcc-send.c adjust the fps to tweak the stream's 
      bitrate. This is what we want.

 * use_video_encoder_rate_control == false &&
   is_low_bandwidth == false
   -> Then there is no bitrate control whatsoever. I'll talk more about 
      this case in the next section.

So to test how the generic bitrate control algorithm behaves and 
establish a baseline I recommend to:

 * Apply the attached patch. The first hunk will let you track the 
   dcc-send's frame drops, while the second will let you force 
   is_low_bandwidth to true simply by setting SPICE_CAP_BR=1.

 * And set SPICE_DISABLE_ADAPTIVE_STREAMING=1 when running spicy.

To make sure it worked check that you have no line containing 
"stream_report" in the server log and check for lines containing 
"generic frame drop".

It can be useful to tail your server log through:

  egrep "(handle_pong|reattach_stream|marshall_stream|set_bit_rate|: jpeg)"


Testing this you'll notice that reducing the available bandwidth results 
in freezes and recovery times that take a dozen second or more. This is 
because it's only after a sufficient backlog has been created (possibly 
a single large video frame in the queue) that the server drops a frame 
which makes the bitrate control aware that something is wrong. This 
issue is common to all algorithms when all they have to rely on is 
server frame drop notifications.

Here are the flaws I see in this generic bitrate control algorithm:
 * The bitrate is adjusted only once per second. So you get drops for 1 
   second before things even have a chance of getting better.

 * Even after being notified of a server frame drop the generic code 
   continues trying to push as much data as before for up to a second.
   But server frame drops indicate the server queue is full and 
   delaying action will delay a return to normal.

 * If you halve the network bandwidth it will take 15 seconds before it 
   halves the framerate from 30 fps down to 15 (one adjustement per 
   second). With the 15 seconds delay the network queue is sure to still 
   be full so it overshoots and drops the framerate down to 1 fps before 
   increasing it again.

 * It never stabilizes the bitrate. So as soon as there are no drops 
   for 1 second it increases the framerate until there are drops again, 
   then it decreases the framerate until there are no drops. Rince, 
   repeat. The result is you never have smooth video.



3. Testing the no-bitrate-control-whatsoever case
-------------------------------------------------

To test this mode, don't set SPICE_CAP_BR=1 when starting the server, 
and start the client with SPICE_DISABLE_ADAPTIVE_STREAMING=1.

Then verify in your server log that the initial bitrate estimate is 
10Mbps or greater (which is very likely, I have seen values as high as 
73Gbps).

What happens then is that in before_reattach_stream() agent->drops is 
never updated in the first loop because is_low_bandwidth == false. Then 
the second loop never updates agent->fps since drops == 0.

So how can this works?

In the logs this mode results in regular 'generic frame drop' messages 
despite agent->fps being set to 30. This seems to imply frames get 
bunched up. But there are in fact way more dropped frames than these 
messages imply.

My theory is that with the network queue full the server stays blocked 
longer trying to send the frames, causing it to ignore most frames drawn 
by the application (or maybe even preventing the application from 
drawing them).

But having the server block on network traffic does not seem right.



4. Testing the video encoders with server-drops-only
----------------------------------------------------

To test this configuration, simply apply the patch on top of the 
previous one and test as you did for case 2.

In this mode the bitrate control is handled by the video encoder. With 
the initial bitrate estimate being too high it will first spend some 
time dropping the bitrate before stabilizing on a value suitably low.

Unlike in the previous cases the image quality will be lowered to 
preserve a reasonable framerate.


-- 
Francois Gouget <fgouget@xxxxxxxxxxxxxxx>
diff --git a/server/dcc-send.c b/server/dcc-send.c
index e33f428..b22c1f4 100644
--- a/server/dcc-send.c
+++ b/server/dcc-send.c
@@ -1692,6 +1692,7 @@ static int red_marshall_stream_data(RedChannelClient *rcc,
 
     if (!dcc->priv->use_video_encoder_rate_control) {
         if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) {
+            spice_debug("generic frame drop fps=%d elapsed=%uns < %uns", agent->fps, (unsigned)(time_now - agent->last_send_time), (1000 * 1000 * 1000) / agent->fps);
             agent->frames--;
 #ifdef STREAM_STATS
             agent->stats.num_drops_fps++;
diff --git a/server/main-channel-client.c b/server/main-channel-client.c
index b47b1e0..42f9bfb 100644
--- a/server/main-channel-client.c
+++ b/server/main-channel-client.c
@@ -509,6 +509,11 @@ void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing *ping,
                        mcc->priv->bitrate_per_sec,
                        (double)mcc->priv->bitrate_per_sec / 1024 / 1024,
                        main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
+        /* FIXME Traffic shaping breaks bitrate detection, cap to 9Mbps */
+        if (getenv("SPICE_CAP_BR")) {
+            spice_printerr("-> capping to 9Mbps");
+            mcc->priv->bitrate_per_sec = MIN(9000000, mcc->priv->bitrate_per_sec);
+        }
         red_channel_client_start_connectivity_monitoring(RED_CHANNEL_CLIENT(mcc),
                                                          CLIENT_CONNECTIVITY_TIMEOUT);
         break;
_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/spice-devel

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]