feat(DASH): Add period caching to speed up manifest parsing#9353
feat(DASH): Add period caching to speed up manifest parsing#9353grabofus wants to merge 9 commits intoshaka-project:mainfrom
Conversation
|
Incremental code coverage: 52.21% |
|
Why create a configuration and not always enable it? |
|
@grabofus thanks for your PR, finding out serious bottleneck and trying to find a way to overcome it! Your change looks OK in general, however let me share another idea I have about it. Personally I don't think we should maintain such a cache. Preferably, I think parser should be a bit smarter and work on some assumptions. The overall expectation in live updates should be that either some data has been removed from the beginning of a file, or added at the end of it. These assumptions are already used silently in other parts of code in shaka, i.e. SegmentIndex, PeriodCombiner or MPD Patch implementation. In my opinion live updates logic should leverage based on those assumptions. Due to my thoughts above, I'm also not sure would we need to calculate hash in a way introduced here, as it's basically DASH agnostic. In doing such live updates we should instead check period characteristics, in particular did any timeline has changed in starting or ending periods. Another idea would be to lazy parse periods, but that's definitely not in scope of this work. That's my general thought that I wanted to share with community. If we decide this approach is a better way to go, I will of course review it through-and-through. cc @shaka-project/shaka-player |
|
Yes and I think DASH specs has a text saying "middle periods" should/shall not be modified after introduced in a live streaming.
There might be live streams available that may change "middle periods" adhoc and a specific app players may cope it well. |
lib/dash/dash_parser.js
Outdated
| const periods = []; | ||
| let prevEnd = seekRangeStart; | ||
| const periodNodes = TXml.findChildren(mpd, 'Period'); | ||
| const enablePeriodCaching = context.dynamic && !!(this.config_ && this.config_.dash && |
There was a problem hiding this comment.
Small nit: I don't see why !! is needed here.
| this.periodCache_.get(cacheKey) : null; | ||
| let reusePeriod = false; | ||
| if (cachedEntry) { | ||
| signature = this.computePeriodSignature_(elem); |
There was a problem hiding this comment.
It's unclear to me why we need both a period cache key and a separate signature.
Let me see if I understand...
We need the key to be cheap, but the signature is more expensive? And we might find a key-match with a different signature, and then we have to treat it as a cache miss?
Are there any cases where we don't have to compute a signature for each and every period? If not, couldn't we move lift the signature computation instead of repeating it in two places?
There was a problem hiding this comment.
The cache key won't change for the period, but it's signature can if its content is modified. So the idea was to be able to quickly find a cached entry then validate if it had any changes by comparing signatures.
lib/dash/dash_parser.js
Outdated
| if (context.period.id && periodDuration) { | ||
| this.periodDurations_.set(context.period.id, periodDuration); | ||
| if (enablePeriodCaching && period.id && signature) { | ||
| this.periodCache_.set(period.id, {signature, period}); |
There was a problem hiding this comment.
Here you set into periodCache_ by period.id. Above, you get into periodCache_ by the return of getPeriodCacheKey_(). Why wouldn't you just use getPeriodCacheKey_() in all cases?
lib/dash/dash_parser.js
Outdated
| * @return {number} | ||
| * @private | ||
| */ | ||
| fnv1a_(str, seed) { |
There was a problem hiding this comment.
I would like to see these (fnv1a_ and hashTXml_) moved to a utility class. It's distracting here in the DASH parser, and also not testable as a private method.
There was a problem hiding this comment.
Moved them into a HashUtils class and added some unit tests 👍
lib/dash/dash_parser.js
Outdated
| * @private | ||
| */ | ||
| hashTXml_(node, seed = 0x811c9dc5) { | ||
| let hash = this.fnv1a_(node.tagName, seed >>> 0); |
There was a problem hiding this comment.
What is the purpose of >>> 0?
There was a problem hiding this comment.
That part was copied from somewhere, I don't know an awful lot about hashing functions so I left that in, but I don't think it makes much difference if we remove it.
|
@grabofus can you review the previous comments? Thanks! |
|
@avelad addressed the comments - sorry for the delay haven't had much time lately to concentrate on this
Unsure if it benefits everyone, or if it could have side effects in some streaming configurations (e.g. if longer SegmentTimelines are used) so I didn't enable it by default. Happy to change it if you think it should be enabled.
I was not sure initially if we can rely on middle periods not changing, but this makes perfect sense and is a more performant way to solve this issue. |
I also don't love the hashing & caching, and would prefer something that uses these assumptions.
Are we going in this direction instead? If so, who will do it? @avelad, @grabofus Thanks! |
Adds period caching to speed up manifest parsing. The aim of this feature is to improve parsing of length multi-period DASH manifests on low power devices.