Skip to content
This repository has been archived by the owner on Jan 22, 2022. It is now read-only.

[documentation] streaming/downloading track too slow makes gvs server close connection early #548

Open
bubbleguuum opened this issue Apr 28, 2017 · 5 comments

Comments

@bubbleguuum
Copy link

bubbleguuum commented Apr 28, 2017

This issue is not a bug, but to document very particular behavior from google streaming servers (gvs) when not downloading/streaming at full speed.

You get the stream URL of a track using get_stream_url().

If you download this URL at full speed, no problem.

Now if you download it at a slower rate (for playback for example), the download may interrupt mid-way or at 70-90% of the download. Why is it so ?

It happens that gvs (the Google Server) interrupts the download unexpectedly if it deems that the client is downloading too slow. But it does so not in a super obvious way and it took me forever to figure out (hence this post). The problem is that it is very aggressive at doing so, and it may happen even if a client downloads at more than 40kb / sec (this is the minimum rate required for playback fo 320 Kbps mp3.

Here's an example capping the download at 50Kb/s. The URL is the one returned by
api.get_stream_url('Tsr37bkzlgcbg33iiuolkop6q3a'). This particular track is useful for testing as it is only 2 min 17sec long:

curl --limit-rate 50K 'https://r1---sn-n4g-cvqs.c.doc-0-0-sj.sj.googleusercontent.com/videoplayback?...' > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
90 5331k 90 4831k 0 0 51125 0 0:01:46 0:01:36 0:00:10 50319
curl: (56) SSLRead() return error -9806

here we can see we get a SSRead() error at 90% of the transfer. This is in fact the server closing the connection unexpectedly and the client getting ECONNRESET in recv, which can be seen better replacing https with http:

curl --limit-rate 50K 'http://r1---sn-n4g-cvqs.c.doc-0-0-sj.sj.googleusercontent.com/videoplayback?...' > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
90 5331k 90 4799k 0 0 51031 0 0:01:46 0:01:36 0:00:10 49519
curl: (56) Recv failure: Connection reset by peer

Here, ECONNRESET is clear, also at 90%.

So if you cannot control directly at which rate the client is streaming (for example if it is black box hardware), what can you do ?

The solution is to proxy the stream, decoupling reading from gvs and client reading.
The proxy reads the stream at full speed (or good speed, say > 100Kb/s) with maximum buffering (ideally full track length but it could be half of that) and the client reads the buffered stream at its own slower pace.

@thebigmunch
Copy link
Contributor

I've repeated your tests with the same song and the same (and even lower rate limits) 10-12 times without an issue. Is this something that happens occasionally or regularly in your testing?

@bubbleguuum
Copy link
Author

bubbleguuum commented Apr 28, 2017

Yes I'm 100% able to reprod it. At 50K, transfer closes at 90%, at 30K at 84% and at 5K at 75% (note that for this last case we are way past the track duration, which makes this issue very weird).

I can reprod this issue with non curl download code (with specific hardware streaming instead at a slow rate) and I observe the same issue (ECONNRESET). On a longer track, cut happens later: for example downloading @ 30K track T7gorf5c4iu7jwb7dk3ezzgnfzy whose duration is 7:02, it cuts at 94%. And on a 1:10 track @ 30K it cuts at 65%. So the shorter the track, the earlier it cut.

Summary of download capped at 30Kb/s:

Track / Duration / Cut at
Tavundblvdfm4zexc6ouofqwpdy / 1:10 / 65%
Tsr37bkzlgcbg33iiuolkop6q3a / 2:17 / 84%
T7gorf5c4iu7jwb7dk3ezzgnfzy / 7:02 / 94%

The percentage at which it cuts is repeatable with a variation possible of a few percents.

Also I know this issue does not happen only on my system as I got user reports of it in the form 'playback sometimes stops xx seconds before then end on hardware yy'.

Here's the verbose output for track @50k, so we can compare (I just edited the ip query field in the stream URL). Can you post yours with a successful download ? It could be interesting to compare the stream URL, in particular the value of the very obscure query fields most of which we probably have no idea what they are for:

curl -v --limit-rate 50K 'https://r1---sn-n4g-cvqs.c.doc-0-0-sj.sj.googleusercontent.com/videoplayback?id=23aa28c6cc6153da&itag=25&source=skyjam&begin=0&upn=BewyAJZrqgU&ei=TSoDWcjGNOqw_gbsia7ADw&o=11552407006682955536&cmbypass=yes&ratebypass=yes&ip=xxx.xxx.xxx.xxx&ipbits=0&expire=1493379751&sparams=cmbypass,ei,expire,id,ip,ipbits,itag,mm,mn,ms,mv,o,pl,ratebypass,source,upn&signature=5D8DFD45CA193ECCCA7F4760384CD4D3352CE136.6A3AE01B75C409C4BD53075395E981B5DCB20468&key=cms1&mm=31&mn=sn-n4g-cvqs&ms=au&mt=1493379327&mv=m&pl=23' > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 77.153.129.108...

  • TCP_NODELAY set
  • Connected to r1---sn-n4g-cvqs.c.doc-0-0-sj.sj.googleusercontent.com (77.153.129.108) port 443 (#0)
  • TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • Server certificate: *.googlevideo.com
  • Server certificate: Google Internet Authority G2
  • Server certificate: GeoTrust Global CA

GET /videoplayback?id=23aa28c6cc6153da&itag=25&source=skyjam&begin=0&upn=BewyAJZrqgU&ei=TSoDWcjGNOqw_gbsia7ADw&o=11552407006682955536&cmbypass=yes&ratebypass=yes&ip=xxx.xxx.xxx.xxx&ipbits=0&expire=1493379751&sparams=cmbypass,ei,expire,id,ip,ipbits,itag,mm,mn,ms,mv,o,pl,ratebypass,source,upn&signature=5D8DFD45CA193ECCCA7F4760384CD4D3352CE136.6A3AE01B75C409C4BD53075395E981B5DCB20468&key=cms1&mm=31&mn=sn-n4g-cvqs&ms=au&mt=1493379327&mv=m&pl=23 HTTP/1.1
Host: r1---sn-n4g-cvqs.c.doc-0-0-sj.sj.googleusercontent.com
User-Agent: curl/7.51.0
Accept: /

< HTTP/1.1 200 OK
< Last-Modified: Fri, 03 Apr 2015 13:51:41 GMT
< Content-Type: audio/mpeg
< Date: Fri, 28 Apr 2017 11:41:12 GMT
< Expires: Fri, 28 Apr 2017 11:41:12 GMT
< Cache-Control: private, max-age=0
< Accept-Ranges: bytes
< Content-Length: 5459591
< Connection: close
< Alt-Svc: quic=":443"; ma=2592000
< X-Content-Type-Options: nosniff
< Server: gvs 1.0
<
{ [16384 bytes data]
91 5331k 91 4895k 0 0 51311 0 0:01:46 0:01:37 0:00:09 52681* SSLRead() return error -9806

  • Curl_http_done: called premature == 1
    92 5331k 92 4911k 0 0 51140 0 0:01:46 0:01:38 0:00:08 50301
  • Closing connection 0
    curl: (56) SSLRead() return error -9806

@thebigmunch
Copy link
Contributor

I don't see anything different in the URL params that shouldn't be (certain hashes/ids). Nor do I see anything different or odd in curl's verbose output for these tests except that yours fail while all of mine succeed.

Should have as many others run these tests to see what happens.

@bubbleguuum
Copy link
Author

bubbleguuum commented Apr 30, 2017

Yes I'd be curious to know if others devs can reproduce it.
I know it happens to others because I got many user reports of it.
And I had to painfully implement full speed proxying to workaround it.
Maybe this issue happens only on some particular network conditions, ISPs or geolocation.
In particular, who knows what ISPs may do in the middle.
For info, I have a fast 200 Mbps cable connection and located in France.

@ghost
Copy link

ghost commented May 9, 2017

I am seeing transfer errors at all speeds, definitely more at high speed (I have around 500 KBytes/S downloads). At high speed, I am seeing a timeout or connection error every few tracks: 4 errors in 16 downloads of the same two tracks in my short test.

Actually I initially thought that I only had errors at high speed and that this was a limitation "feature".

But I also saw an error at playing speed while doing tests today (one track timeout in 2 hours of playing, 28 downloads of the above tracks).

I am in Paris, and my conclusion at this point is that Google Music is just unreliable in France...

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants