kiwix / kiwix-apple

Kiwix for offline access on iOS and macOS
https://apple.kiwix.org
GNU Lesser General Public License v3.0
434 stars 69 forks source link

Videos are pre-loading in full #744

Open BPerlakiH opened 2 weeks ago

BPerlakiH commented 2 weeks ago

After checking it, and benchmarking, it turned out that the videos (webm) are pre-loaded, when the webpage is loaded in full ( in one http request we load from start to finish). This is sub optimal, and with larger videos it can cause unecesary waiting time for the user. The time it takes on macOS, using an external usb-c drive:

content loaded: video/webm, 6,1 MB: 173.9137916592881 ms
content loaded: video/webm, 4,5 MB: 130.82945832866244 ms
content loaded: video/webm, 5,7 MB: 157.31854166369885 ms
content loaded: video/webm, 6,6 MB: 193.12754168640822 ms
content loaded: video/webm, 15,6 MB: 422.17312499997206 ms
content loaded: video/webm, 5,7 MB: 160.3915833402425 ms
content loaded: video/webm, 18,9 MB: 525.2033333526924 ms
content loaded: video/webm, 49,3 MB: 1338.0876666633412 ms
content loaded: video/webm, 48,2 MB: 1244.5944999926724 ms
content loaded: video/webm, 49,3 MB: 1278.1917083193548 ms
content loaded: video/webm, 69,6 MB: 1844.8072916653473 ms
content loaded: video/webm, 86,2 MB: 2274.9759999860544 ms
content loaded: video/webm, 49,5 MB: 1296.8016666709445 ms
content loaded: video/webm, 64,8 MB: 1680.7168333325535 ms
content loaded: video/webm, 52,5 MB: 1368.049125012476 ms
content loaded: video/webm, 49,3 MB: 1192.994874989381 ms
content loaded: video/webm, 88,9 MB: 2392.5675000064075 ms

From this it is obvious that the loading time, can go over 2 seconds, if the video is larger.

benoit74 commented 2 weeks ago

I'm sure it can also go over 2 seconds if you have a poor HDD and/or an ("old") Intel machine.

Did you even included a hour long video in your test set?

It doesn't change anything to the issue aside from pushing emphasis on how important it is to solve this.

BPerlakiH commented 6 days ago

Summary

Comparing Safari and Chrome browsers on macOS (outside of the app)

With the current html setup we have on the html video tag:

data-setup='{"techOrder": ["html5", "ogvjs"],

This makes video rendering hand over to html5 in both browsers. This mode is more or less equal to the a pure html5 video implementation without any javascript (without videojs and videojs-ogvjs). The difference being is that Safari pre-loads the whole content of the video, starting at page load, whereas Chrome handles it better, and starts loading when it is played. The preload attribute is ignored by Safari, and not helping in this regard. In this scenario seeking works as expected in both browsers.

Changing it to:

data-setup='{"techOrder": ["ogvjs"],

and using the python default Simple server, it fixes the issue of initial preload: Safari is not pre-loading before the play button is pressed, similarly Chrome. Since the range request seems not to be handled by the server, it results in the following:

HTML5 only, do not create Range requests:

I did try it in a couple of ways, even returning 206 responses with the first range, but the player was not requesting more ranges. The preload argument of the html5 video tag doesn't make any difference in this regard.

Removing HTML5, using OGV-JS and Range requests:

After some tweaking in the application, I forced it to use data-setup='{"techOrder": ["ogvjs"], meaning it is not using the HTML5 player. It indeed creates range requests, but it is not working smoothly. I did observed the following ranges requested:

Summary
URL: kiwix://93A5A6DA-C6BF-95B2-23AC-B67C79457880/videos/96462/video.webm
Status: 206 Partial Content
Source: —
Initiator: 
ogv.js:2:91642

Request
Accept: */*
Range: bytes=0-1048575
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) > > Version/17.4 Mobile/15E148 Safari/604.1

Response
Content-Length: 16313408
Content-Range: bytes 0-1048575/16313408
Content-Type: video/webm
Summary
URL: kiwix://93A5A6DA-C6BF-95B2-23AC-B67C79457880/videos/96462/video.webm
Status: 206 Partial Content
Source: —
Initiator: 
ogv.js:2:91642

Request
Accept: */*
Range: bytes=1048576-2097151
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1

Response
Content-Length: 16313408
Content-Range: bytes 1048576-2097151/16313408
Content-Type: video/webm

These ranges seem OK to me, but the video is not playing and not seekable.

AVPlayer is not playing WebM

I have tested it and AVPlayer (the native iOS player) is not supporting WebM at the moment. In order to use this route, we would need to use an external player library.

Performance on devices

I measured again, how it behaves on device with a 1.5 hour long video content.

iPhone

In the worst case scenario, the 1.5 hour video was loaded in ~600 ms, subsequent loads of the same video are below 50 ms.

iPad

I can conform the HTML player is not working on iOS 17.1 (and possibly neither below that), but it does work on 17.4.1. The video load time is similar: ~550 ms.

Conclusion

At the moment it seems the best working option is the HTML player provided by Apple, which works reasonably well on new iOS / macOS versions. At the moment, I do not see a way to support range requests, that could improve on the user experience. On end devices, even the 1.5 hour long video was loading in a reasonable time. This seems to be our best possible option for the moment.

kelson42 commented 6 days ago

@BPerlakiH Not sure about:

I find the last comment very difficult to be exploitable because:

BPerlakiH commented 4 days ago

@kelson42

How looks the fix concretely?

I am writing down my findings here, I do not have a fix for it (yet).

You seem to assume that you have high bandwidth between browser and server.

In our situation we are reading the video out of a local ZIM file, the network / bandwidth can be ignored in this case.

Playing a video with ojv.js creates range requests and this is not working even with a web server?

I did test this with python default http server: Well, it is playing the video, but the video is not "seek-able", we have the following error in the console, when trying to seek:

VIDEOJS: 
Error: Cannot seek a non-seekable stream — 
"Video is not ready. (Video.js)"

This is the request / response:

Summary
URL: http://localhost:8000/videos/onehour/video.webm
Status: 200 OK
Source: Network
Address: ::1:8000
Initiator: 
ogv.js:2:91642

Request
GET /videos/onehour/video.webm HTTP/1.1
Accept: */*
Accept-Encoding: identity
Accept-Language: en-GB,en;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Cookie: _ga=GA1.1.1067747901.1681038766
Host: localhost:8000
Pragma: no-cache
Range: bytes=0-1048575
Referer: http://localhost:8000/le-farine-vs-le-boiserie.html?lang=undefined
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15

Response
HTTP/1.1 200 OK
Content-Length: 168736330
Content-Type: video/webm
Date: Tue, 14 May 2024 19:46:04 GMT
Last-Modified: Mon, 06 May 2024 07:34:24 GMT
Server: SimpleHTTP/0.6 Python/3.12.3

My understanding of this is: javascript requests the first range, but the server sends back the whole video file. This is what I meant: the web server is not sending back the expected Response. "Since the range request seems not to be handled by the default python server".

I keep checking what else I can improve in this regard.

kelson42 commented 4 days ago

You seem to assume that you have high bandwidth between browser and server. In our situation we are reading the video out of a local ZIM file, the network / bandwidth can be ignored in this case.

Yes and no, Browsers on iOS/macOS should work as well with Kiwix Server (I though this were something you were checking as well) and you can not assume everything is local (you can have a file stored on a network drive for example).

kelson42 commented 4 days ago

Playing a video with ojv.js creates range requests and this is not working even with a web server?

I did test this with python default http server: Well, it is playing the video, but the video is not "seek-able"

it's not because a piece of software says "not seekable" that the http has to be unable to handle range requests. This can have other root causes. In addition, if your http server is not able to handle range requests, your whole testing is worthless as you use it as reference.

kelson42 commented 4 days ago

@benoit74 @rgaudin Can you please confirm the Pythin http server handles range request properly?

rgaudin commented 4 days ago

@benoit74 @rgaudin Can you please confirm the Pythin http server handles range request properly?

It doesn't. It's a very basic HTTP server meant for educational purposes.

kelson42 commented 4 days ago

@benoit74 @rgaudin Can you please confirm the Pythin http server handles range request properly?

It doesn't. It's a very basic HTTP server meant for educational purposes.

Then I hardly understand why we recommend to use it as reference?! In particular for this ticket?!

rgaudin commented 4 days ago

I did not recommend it. I specifically mentioned nginx and caddy. I believe @benoit74 suggested it to test ogvjs in general, not for this specific issue.

kelson42 commented 3 days ago

OK, then @BPerlakiH you need to redo your experience with a HTTP serve handling range requests properly.

I don't know exactly what went wrong here... but I really wonder how we have managed to waste so much time for almost no learning.

BPerlakiH commented 3 days ago

Ok, I am sorry for the confusion, will look into nginx / caddy then.

benoit74 commented 3 days ago

Python server was original solution used by @BPerlakiH ; I provided instruction for both python and Caddy with the test ZIM, so it should be straightforward to use Caddy. I put them here again for reference.

docker run -d -p 80:80 -v $PWD:/usr/share/caddy caddy
BPerlakiH commented 8 hours ago

Please see my summary of what is currently working / supported and what is not under: #765.