elgatito / plugin.video.elementum

Elementum add-on for Kodi. Development of this addon has been stopped!
http://elementum.surge.sh
MIT License
490 stars 160 forks source link

[bug] [enhancement] Excessive piece redownloading when skipping; Fix:Keep last 30s in RAM buffer? #837

Closed 21starcade closed 11 months ago

21starcade commented 3 years ago

First off, the plugin works amazing for continuous playback even of 4k videos with just 250-300MB of RAM! Great work, no issues there.

System

Arm32, Android 7, api 25, 300MB max free RAM, RAM-only storage, Any Kodi version, any video cache setting

Steps to reproduce/Current behavior

Skipping back while playing videos, even just 10 seconds back, causes Kodi to freeze playback while Elementum needlessly reconnects peers to redownload the most recently played chunks again and again. This is very problematic when you miss a line of dialogue or watch instructional videos where you have to go back and wait repetitively.

Eg, due to peering overhead, skipping back 10s can cause a 10s freeze for an 8-mbit video and a 20-25s freeze** for a 20-Mbit video despite maximum piece availability, top speeds and having enough RAM to buffer 300/120s of video!

Expected Behavior

Always protect the last 30s(or user-set value) played in Elementum buffer (~30-75MB reserve) to allow responsive back skipping.

Possible Solution/Enhancement Pseudocode using a Forward+Recent in-RAM buffer

  1. Provide a user option to set an amount of recently played seconds (default:31s) to always be kept in an Elementum "Recent Reserve Buffer", in RAM.
  2. Map seconds to torrent pieces and define the Recent Reserve buffer size/(number of pieces) for common bitrates and set the Elementum cache to fit both the Forward AND Recent buffers. Hence, no extra redownloading.
  3. Implement a more Pragmatic Download Strategy by downloading the nearest 4 minutes (default: 60s back, 180s forward) 2.0. Prioritize next 30-60s of playback pieces into an Elementum "Forward Buffer" (same as current intent). 2.1. If lots of RAM is free, increase "Recent Reserve Buffer" from 30s to 60s and fill it. 2.2. If free RAM still, keep increasing the "Forward Buffer" with next pieces till RAM is full. 2.3. Move all buffers start times/pieces according to user playback time.

Context, goals

  1. Creating a "Recent" as well as a "Forward" buffer can potentially reserve 1 minute of recently played video and up to 3 minutes of upcoming 8-mbit video into a highly responsive, seekable 4-minute buffer on devices with as little as 256MB of free RAM! This should be plenty of cache for all kinds of nearby seeking, except big jumps.

  2. Missing a scene or a quick rewind to take notes in-play will be joyfully immediate, as if the entire file is stored.

  3. Finding an exact position in a file will be much faster when you can go not just forward but also backward fast.

  4. Piece redownloading/peer rediscovery will be minimized, swarms and networks will be healthier, clients will distribute new pieces faster and more frequently.

Final thoughts and quick fixes

I am not familiar with the interaction between your libtorrent download strategy, the Elementum pieces cache and the Kodi video buffer, BUT an easy fix could be as simple as "do not drop the first buffered pieces" in Elementum's cache. Alternatively, the libtorrent config might be tweaked. Certainly, the Kodi video cache is NOT related.

Hope this helps. :))

P.S. Ideally, Elementum/libtorrent will download next few seconds for smooth playback and then try to download pieces around Kodi's default skip steps (eg, +/-10s, +/-30s, +/-1 min, +/3 min, ...). With sufficient bandwidth/peers and RAM, you'll be able to search through an entire video instantaneously. Eg, 15 skip steps w 5s playable each can be buffered with just 75MB RAM. I realize this may be hard to inplement so I am only suggesting reserving last 30s of pieces.

elgatito commented 3 years ago

@21starcade You cannot know how many megabytes are required for specific part of a video. Bitrate is not constant per whole video stream. Elementum is operating in bytes and that is the only possible measurement, not seconds.

21starcade commented 3 years ago

@21starcade Bitrate is not constant per whole video stream. Elementum is operating in bytes and that is the only possible measurement, not seconds.

Thanks for the clarification, elgatito. As far as I understand, pieces are downloaded and sent to Kodi in 3 stages:

  1. libtorrent -> 2. Elementum RAM cache -> 3. Kodi RAM video cache

Currently, once bytes are sent from 2. to 3., they get immediately dropped from 2. (ie bytes are cut+pasted and deleted >playback), which always causes redownloading on backSkip.

Let's say we have an 8-Mbit video, a torrent piece is 16MB on average, Elementum cache is 256MB RAM. Instead of using times/seconds, we can create 32MB of Recent cache by simply not dropping the last 32MB that Elementum passed to Kodi's video cache. This can be expressed as a fraction of user-selected cache size. In this example, recent/whole cache = 1/8 (12.5%).

As a function, I imagine something like: Keep last 32MB (=2 pieces), sent from Elementum to Kodi video cache, into 1/8 of Elementum's cache.

Essentially, Elementum would never delete the most recently played 32MB of frames, this way creating a 32MB Recent buffer on-the-fly +preventing unnecessary redownloading for the last 30s (avg for an 8-mbit) video. Then, the usual download strategy (download pieces ahead) can be continued until the other 7/8 of Elementum's cache are filled. Users can tweak the cache size and the reserved fraction to match their type of systems/videos.

Can such an option be easily enabled with a simple "don't delete the last played 32MB (or user-set value) from ELem cache" code change?

elgatito commented 3 years ago

@21starcade

Currently, once bytes are sent from 2. to 3., they get immediately dropped from 2. (ie bytes are cut+pasted and deleted >playback), which always causes redownloading on backSkip.

No, piece is removed from memory only when we need to save new piece. Then we remove the oldest piece. It is usual that Kodi has several active readers of the same file (for example one for video, one for audio stream), so you have few readers and you have to split all the limits/sizes per each reader.

21starcade commented 3 years ago

@elgatito

No, piece is removed from memory only when we need to save new piece. Then we remove the oldest piece.

I see. Maybe the issue is in the interaction between the buffers/readers. Eg, they may be waiting synchronously on each other and thus causing freezes until data is dropped/copied.

I any case, the trouble is in the last 16-32MB played, which are right now at least partially dropped/replaced very quickly, because every time a video is skipped back 10-30s, there's a 10s freeze, even if the entire Elem cache is full, at max speed.

Can't we just identify the currently playing piece (ie the last piece passed from Elem to Kodi) and lock/keep the 2 pieces before/around it in memory?

21starcade commented 3 years ago

Did some testing, and interestingly, freezes are more likely after the file has been played for a minute , as if there's a problem between the Kodi cache and Elem cache.

When you're seeking quickly with minimal playback (eg 5s) freezes are shorter or less frequent. Is there some sort of waiting period between Kodi and Elem cache when you seek or when a buffer is full?

elgatito commented 11 months ago

When you're seeking quickly with minimal playback (eg 5s) freezes are shorter or less frequent. Is there some sort of waiting period between Kodi and Elem cache when you seek or when a buffer is full?

No, there is no time lag between Kodi and Elementum. Kodi is controlling what to request from Elementum. Elementum will return requested data if it is already downloaded, or request missing pieces if needed and then Kodi will need to wait once that piece is downloaded.

Elementum cannot know which piece will be requested by Kodi and when, but Elementum is trying to sequentally download pieces.

Once you seek player - you will land somewhere and Kodi will request that piece from Elementum and Elementum will try to resort pieces we want to download and invalidate ones that are not required anymore.

If you want to get back - then your position changes and we will need to resort pieces again.

Every time you do seeks - we need to download new pieces. With heavy content that takes a while, of course.

The only good solution to avoid lags while seeking - use file storage, that we are prioritizing pieces but not removing them.

elgatito commented 11 months ago

I'm closing this issue as I believe there is nothing we can do.