Skip to main content

pop_range Addendum: Revisions from Reviewer Feedback

· 6 min read
Austin Kim
Staff Software Engineer, System programming and media framework specialist

This document supplements, rather than replaces, the original pop_range design document. It covers three points a reviewer raised after that document was published, the verification done for each, the resulting code changes, and the measured before/after behavior. The original document's design rationale, history section, and prior-art research are unchanged and still apply; only the three areas below have moved since that write-up.

0. Context

The original pop_view<A>::operator* returned a copy (value_type), the supported container shape was fixed to pop() + top()/front() (i.e. only stack, queue, priority_queue), and size()/sized_range was deliberately left unimplemented. A reviewer pointed out that all three of these were more conservative than necessary. Each point below was checked independently — against the actual wording of the relevant concepts, and against a compiled, sanitizer-clean test — before being adopted.

Extending Ranges and Views to Non-Iterable Container Adaptors

· 14 min read
Austin Kim
Staff Software Engineer, System programming and media framework specialist

1. Motivation

std::stack, std::queue, and std::priority_queue are container adaptors: thin wrappers around an underlying sequence container (vector, deque, list) that expose a deliberately restricted interface — push, pop, top/front, empty, size. None of them expose begin()/end(). This is not an oversight; it is a design decision inherited from the original SGI STL, whose documentation states outright that priority_queue "does not allow iteration through its elements." The rationale is that the adaptor's whole contract is "you may only ever see the current top/front element," and exposing arbitrary iteration would leak the underlying container's storage order (vector's heap layout, in the case of priority_queue), which is not part of the abstraction.

The consequence is that these three types cannot participate in the modern iteration and composition idioms C++ has accumulated since C++11 — range-based for, and since C++20, the <ranges> view/pipe machinery — because both require some notion of begin()/end(). This document describes pop_range, a small header-only library that closes that gap by giving these adaptors a well-defined, honestly-named destructive view: iterating it doesn't just read the adaptor, it drains it, one pop() per increment.

Preloading Multiple ExoPlayer Instances Without Exhausting the VPU's MPS Budget

· 21 min read
Austin Kim
Staff Software Engineer, System programming and media framework specialist

TL;DR

  • The problem: preloading several ExoPlayer instances so playback can start instantly hits the VPU's shared decoder admission budget — creating too many MediaCodec hardware decoders concurrently gets rejected (OMX_ErrorInsufficientResources / MediaCodec.CodecException, ExoPlayer error codes 4001/4003) — even though only one video is ever actually playing at a time.
  • Why it happens: ExoPlayer sets KEY_OPERATING_RATE to the real content frame rate — effectively "1x," the actual playback rate — on every decoder it creates, whether that decoder is actively playing or just sitting preloaded and idle (Section 5), and it never sets MediaFormat.KEY_PRIORITY at all (confirmed absent from MediaCodecRenderer.java/MediaCodecVideoRenderer.java). The net effect: the VPU's admission accounting has no signal telling it "this decoder isn't playing yet" and treats every created instance as if it needs guaranteed real-time decode throughput the moment it's created, not just once it's actually feeding frames. (Qualcomm's own kernel driver control technically defaults KEY_PRIORITY to non-realtime — Section 3.5 — but the resource exhaustion this document exists to solve is consistent with the vendor's closed-source Codec2/OMX HAL layer not preserving that default in practice; unverified from here, flagged as a caveat throughout.)
  • The fix: explicitly set KEY_PRIORITY = 1 (non-realtime) and a low KEY_OPERATING_RATE on every decoder in the preload pool while it's idle, then flip both to realtime / full rate only on the one instance that's actually about to play (Section 6). Non-realtime load is charged at roughly resolution × 1 instead of resolution × fps in the admission math (Sections 3.1–3.3), and non-realtime instances are excluded entirely from the per-core clock-fit check that gates new decoder admission (Section 3.6). For 30fps video, that's roughly a 30× difference in admission cost between an idle non-realtime decoder and one actively playing at realtime priority (worked example, Section 3.4) — meaning, in principle, on the order of 30× more preloaded decoder instances can coexist in the same hardware budget than the naive "every decoder is realtime by default" approach allows.