Why this matters

The device matrix is where streaming budgets quietly double. A founder scopes "an app" and discovers the catalogue must reach a browser, an iPhone, an Android phone, an Apple TV, a Fire TV stick, a Roku, a Samsung TV, and an LG TV — eight targets with four different programming languages between them — each with its own store, its own certification, and its own bugs. Treat them as eight unrelated projects and you pay to build the same login screen, the same recommendation rows, and the same analytics eight times, then pay again every quarter to maintain all eight. This article explains how a unified player strategy keeps the shared parts shared and confines the per-platform cost to the parts that truly differ, so you can have an accurate conversation with your engineers and budget the real number. It builds on the OTT client matrix, which maps every screen you must cover, and video player engineering, which explains what a player does inside.

The device matrix is the tax

Start with the thing teams underestimate: the breadth of the matrix. To reach a mainstream audience in 2026 a streaming service realistically has to cover the web, iOS (iPhone and iPad), Android phones and tablets, Apple TV (tvOS), Amazon Fire TV, Android TV / Google TV, Roku, Samsung TVs (the Tizen operating system), and LG TVs (the webOS operating system) — and often games consoles on top of that. You cannot quietly skip the big ones. In US connected-TV households, Roku's platform holds roughly 28% of usage and Samsung's Tizen about 23% (Parks Associates, April 2026), with Fire TV, LG webOS, and Vizio filling the middle — so dropping "just the TVs" means walking away from most living-room viewing.

Each of those targets speaks a different language. The web is JavaScript or TypeScript. iOS and tvOS are Swift. Android, Fire TV, and Android TV are Kotlin. Roku has its own language, BrightScript, inside its own user-interface framework, SceneGraph. Samsung's Tizen and LG's webOS are HTML, CSS, and JavaScript apps, but each with its own television-specific media interface. Build a complete, independent player and application for every one and you are running, in effect, eight small software products that happen to show the same films. That duplication — paid once to build and forever to maintain — is the device-matrix tax, and a unified strategy exists to shrink it.

The good news: the foundation is already unified

Here is the reassuring part, and it is the scalability lever that makes everything else affordable: the most expensive asset — the video itself — is built once and reused on every screen. Modern streaming encodes each title into one set of files using the Common Media Application Format, usually shortened to CMAF (the fragmented-MP4 container standardised as ISO/IEC 23000-19), and protects them with the cbcs scheme of Common Encryption (ISO/IEC 23001-7). That one encrypted package is delivered as both an HTTP Live Streaming (HLS, IETF RFC 8216) playlist for Apple devices and an MPEG-DASH (ISO/IEC 23009-1) manifest for everything else, and the same segments play under all three content-protection systems — Widevine, PlayReady, and FairPlay — from the same keys. We cover that convergence in packaging: CMAF, HLS, and DASH from one mezzanine and multi-DRM: one workflow, every device.

Why does this matter for the player strategy? Because it means every client app, in every language, is pointed at the same media URLs and the same license URLs. No platform gets a special encode. The hard, costly part of streaming — producing and protecting the catalogue at scale — is unified by standard before a single app is written. The job left to "the player on every screen" is therefore narrower than it looks: not redo the streaming, but present the same stream well on each device. Hold that distinction; the rest of the article builds on it.

Four layers, four different answers

The mental model that prevents eight redundant projects is to stop thinking about "apps" and start thinking about layers. A streaming client is a stack of four, and each layer has a different correct answer to "share or rebuild?"

A four-layer stack showing which parts of a streaming client are shared across all devices and which are built per platform. Figure 1. The four layers of a streaming client. The bottom and top layers are shared by every device; only the two middle layers carry real per-platform cost.

The bottom layer is the content and backend — the CMAF files, the manifests, the license servers, and the application programming interfaces (APIs) that serve the catalogue, check entitlement (whether this viewer is allowed to watch), and receive analytics. This layer is fully shared by definition; it lives on your servers and every client talks to it the same way.

The next layer up is the player engine — the component that fetches segments, decides which quality to play as the network changes (adaptive bitrate, or ABR), manages the buffer, talks to the device's DRM, and decodes frames. This is the layer that cannot be fully unified, because each operating system ships its own engine and its own DRM, and we will spend most of this article here.

Above that sits the application interface — the rows of artwork, the search screen, the playback controls, the settings. This layer is built per input model rather than per platform: a ten-foot television interface driven by a remote control is a different design from a touchscreen phone, which is different again from a mouse-and-keyboard browser. You typically have three interface idioms, not eight.

The top layer is the business logic — sign-in, the recommendation rows' ordering, watch-history, parental controls, the analytics rules. None of this is platform-specific; it is the same decisions on every screen, and it is the layer teams most often, and most wastefully, rebuild eight times. A unified strategy pushes as much of this as possible down into the shared backend and shares the rest as a library.

So the unified strategy, in one sentence: share the bottom and top layers completely, share the interface across three input idioms, and confine genuine per-platform work to the player engine.

The player engine: where you cannot fully unify

Now the hard layer. The reason there is no single video player that runs everywhere is that the operating systems do not let one exist for premium, protected content. Each platform pairs its own player engine with its own hardware-backed DRM, and you must use the native pairing to get a license for protected video and to reach the efficient hardware decoder.

On the web, you do get a near-universal engine, and it is worth understanding why. Browsers expose two World Wide Web Consortium (W3C) standards: Media Source Extensions (MSE, a W3C Recommendation) lets JavaScript feed media segments to the <video> element, and Encrypted Media Extensions (EME, a 2017 W3C Recommendation) lets that JavaScript talk to whatever DRM the browser provides. On top of those, open-source players — Shaka Player, hls.js, and dash.js — give you one codebase that plays adaptive, protected video in Chrome, Edge, Firefox, and Safari. That is real unification, but only within the browser. The crucial catch that surprises teams: a web player such as Shaka Player is JavaScript built on browser APIs, so it does not run natively on iOS, Android, or Roku. "We'll just use Shaka everywhere" is the single most common wrong turn in this whole topic.

Off the web, you use each platform's native engine. On Apple's iOS and tvOS, that is AVPlayer (part of the AVFoundation framework), which plays HLS and uses FairPlay for protection. On Android, Fire TV, and Android TV, it is ExoPlayer, now shipped inside Google's Jetpack Media3 library, which plays DASH and HLS and uses Widevine. On Roku, it is the Video node inside the SceneGraph framework, driven by BrightScript, with PlayReady or Widevine. On Samsung Tizen, your app is HTML but the video runs through Samsung's native AVPlay interface, which handles adaptive streaming, 4K, and PlayReady or Widevine. On LG webOS, it is the platform's own media element inside a web app. Six engines, three languages, three DRMs — and one shared set of CMAF files feeding all of them. Each platform gets its own deep treatment in iOS and Android playback, Apple TV / tvOS and Fire TV, Roku development, and smart TV apps: Tizen and webOS, while web playback covers the browser engine in full.

The table is the coverage map. Read the "Shares code with web?" column: it is the honest measure of how much of your web player you can actually reuse on each platform, and for the native platforms the answer is "almost none of the engine."

Platform Player engine Language DRM Shares engine code with web?
Web browser Shaka / hls.js / dash.js (MSE + EME) JS / TS Widevine, PlayReady, FairPlay n/a (this is the web engine)
iOS / iPadOS AVPlayer (AVFoundation) Swift FairPlay No
Apple TV (tvOS) AVPlayer (AVFoundation) Swift FairPlay No — but shares with iOS
Android / Android TV ExoPlayer (Media3) Kotlin Widevine No
Amazon Fire TV ExoPlayer (Media3) Kotlin Widevine No — but shares with Android
Roku SceneGraph Video node BrightScript PlayReady / Widevine No
Samsung (Tizen) AVPlay (native) in HTML app JS PlayReady / Widevine Partial (HTML shell only)
LG (webOS) webOS media element in HTML app JS PlayReady / Widevine Partial (HTML shell only)

A comparison matrix of player engines, languages, and DRM across web, iOS, Android, Roku, Tizen, and webOS. Figure 2. The player-engine matrix. Apple and Android families each share an engine within themselves; the TV operating systems each stand alone.

Two consolidations soften the picture. Apple's iOS and tvOS share AVPlayer, so one Swift player serves iPhone, iPad, and Apple TV. Android, Fire TV, and Android TV all run ExoPlayer, so one Kotlin player serves the whole Android family. That collapses the engine count from eight platforms to roughly five engine families: web, Apple, Android, Roku, and the two HTML-TV systems (which share the web-app shape but not the media interface). Five is the realistic floor for the player layer — not one, and not eight.

Cross-platform frameworks: what they actually save

At this point someone always asks: "Doesn't React Native or Flutter solve this — write once, run everywhere?" The answer is yes for the interface and the business logic, and no for the player engine — and confusing the two is expensive.

A cross-platform framework — React Native (JavaScript/TypeScript) or Flutter (the Dart language) — lets you write one user interface and one set of business logic and run it on both iOS and Android. That is a genuine saving on two of your four layers. But these frameworks do not decode video themselves. Their video components are thin bridges to the same native engines underneath: a React Native video component hands playback to AVPlayer on iOS and ExoPlayer on Android; Flutter's video plugins do the same. So a framework does not remove the native player; it wraps it. You still depend on AVPlayer and ExoPlayer, their DRM behaviour, and their quirks — you have just shared the screen around them.

There is a second limit that matters for OTT specifically: React Native and Flutter target phones and tablets, not the living-room platforms. Roku runs only BrightScript; Tizen and webOS run their own HTML app models. A mobile cross-platform framework does nothing for those screens, which is where, as the share numbers showed, most television viewing happens. So a framework can unify the mobile corner of the matrix, while the TV corner still needs native work.

A different tool, Kotlin Multiplatform, takes the approach many large streamers prefer: share the non-interface logic (networking, entitlement, playback orchestration) as one Kotlin library reused across platforms, while keeping a fully native interface and the native player on each. It targets the top layer of our stack — the business logic — rather than the interface, and large apps (Netflix and Airbnb among them) use it to share core logic while shipping native screens. The lesson across all three tools is the same: frameworks share the layers around the engine; the engine stays native. Choose a framework to cut interface and logic cost, never in the belief that it makes one video player run on every screen.

The realistic unified architecture

Put the layers together and a clean architecture falls out. Make the backend the single source of truth and have it hand every client the same instructions. When a viewer presses play, the server returns one small "playback descriptor" — the same shape for every platform — telling the client where the media is, where to get a license, and how to behave:

{
  "manifest": {
    "hls":  "https://cdn.example.com/title/abc/master.m3u8",
    "dash": "https://cdn.example.com/title/abc/manifest.mpd"
  },
  "drm": {
    "fairplay":  "https://lic.example.com/fairplay",
    "widevine":  "https://lic.example.com/widevine",
    "playready": "https://lic.example.com/playready"
  },
  "startPosition": 0,
  "adConfig": "https://ads.example.com/vmap?title=abc",
  "analyticsBeacon": "https://qoe.example.com/collect"
}

Every client — Swift, Kotlin, BrightScript, web — receives that identical descriptor and implements a thin player adapter: a small piece of per-platform code that takes the descriptor, picks the right manifest and DRM for its device (HLS + FairPlay on Apple, DASH + Widevine on Android, and so on), drives the native engine, and emits the same analytics events back. The adapter is the only part that is genuinely rewritten per platform. Everything above it (the interface, sharable across three input idioms) and below it (the backend and the encode-once package) is shared.

A decision tree for choosing native, cross-platform framework, or web-wrapped builds per device target. Figure 3. Choosing the build approach per target. The branch points are the device family, the team's existing skills, and how demanding the interface is.

This is also where the build-approach decision lives, and it is made per device family, not once for the whole product. For phones and tablets, a cross-platform framework is often the right call if the team's skills fit and the interface is not too demanding, because it shares the mobile interface and logic. For the Apple and Android TV-and-phone families, native gives the best playback control and is frequently worth it. For Roku, Tizen, and webOS there is no choice — they are native by definition. The unifying thread is not "one technology"; it is "one backend contract and one set of business rules, with a thin native adapter wherever the platform demands it."

The maintenance cost nobody budgets for

The build is the cheap part. The device matrix is a recurring cost, because every platform changes underneath you: operating systems get new versions, devices ship with new quirks, app stores require periodic re-certification, and DRM and security requirements shift. The 2026 change to PlayReady on Samsung televisions — which forced apps to migrate to a newer DRM version on a deadline — is the model of how a single vendor decision creates unplanned work across a slice of your matrix overnight (we cover it in the PlayReady-on-Samsung 2026 migration). Budget for maintenance as a standing line item, not a one-off.

Work the arithmetic out loud, because it is the number that justifies the whole strategy. Say one platform's player-plus-app takes 6 engineer-months to build and 1 engineer-month per quarter to maintain. Cover eight platforms as eight independent codebases and the build is 8 × 6 = 48 engineer-months, and maintenance is 8 × 4 = 32 engineer-months every year, forever. Now move the shared layers — the backend contracts, entitlement, analytics, and the business logic — into one core that all eight reuse. If that shared core is 40% of each client, you build it once (0.40 × 6 = 2.4 months) and each client shrinks to the remaining 60% (3.6 months): total build = 2.4 + 8 × 3.6 = 31.2 engineer-months, about a third less. The bigger win is maintenance: a bug fixed in the shared core is fixed on all eight screens at once, so the recurring bill falls and — more importantly — the platforms stop drifting apart into eight subtly different products.

A cost ramp comparing eight independent codebases with a shared-core-plus-adapters approach as platforms are added. Figure 4. Independent codebases grow cost linearly with every platform added; a shared core flattens the slope because each new platform is only an adapter plus an interface.

The realistic team

Translate the engine families into people and you get the honest staffing answer. To keep the full matrix healthy you need skills for: the web player (JavaScript), the Apple family (Swift, iOS + tvOS), the Android family (Kotlin, phones + Fire TV + Android TV), Roku (BrightScript — a genuinely separate specialism), and the Samsung/LG HTML-TV apps (JavaScript, but with TV-specific testing on real hardware). Sitting across all of them you want one owner for the shared playback contract and quality-of-experience instrumentation, so the descriptor, the adapters' behaviour, and the analytics stay consistent. That is why even a "lean" OTT client team rarely drops below five or six specialised skill areas, and why a device lab of real TVs and sticks is a fixed cost, not a luxury — many platform bugs only appear on the actual hardware. Player-side measurement, which keeps all these clients honest, is the subject of player QoE instrumentation.

Common mistakes — the five that multiply the device-matrix tax. First, believing one player runs everywhere: a web player such as Shaka is browser-only and will not run native on iOS, Android, or Roku — plan five engine families, not one. Second, expecting React Native or Flutter to unify the video engine: they bridge to the same native AVPlayer and ExoPlayer and cover phones only, not Roku, Tizen, or webOS. Third, rebuilding business logic per platform instead of sharing it through one backend contract — that is the duplication that doubles the bill. Fourth, skipping the living-room platforms because they are harder, when Roku and Samsung are where most TV viewing is. Fifth, budgeting the build but not the maintenance, then being blindsided by an OS update or a DRM migration like the Samsung PlayReady 2026 change.

Where Fora Soft fits in

A unified player strategy is, above all, a scale-and-cost decision: it is what lets one catalogue reach millions of viewers across a dozen device types without a dozen disconnected teams. Fora Soft has built video streaming, OTT/Internet TV, video conferencing, e-learning, and telemedicine apps since 2005 — 625+ projects for 400+ clients across 20+ years — so the architecture here (one encode-once package, one backend contract, native player adapters per platform, and a shared QoE layer over the top) is the day-to-day of our streaming work across web, mobile, and the smart-TV matrix. We are vendor-neutral: we translate your device targets and content-owner rules into the right share-or-build line per layer, rather than selling a single cross-platform SDK as a cure-all.

What to read next

Call to action

References

  1. Common Media Application Format (CMAF) for segmented media — ISO/IEC 23000-19. ISO/IEC. The single fragmented-MP4 package that serves both HLS and DASH from one encode, the foundation that lets every client use the same media. Tier 1 (primary standard). https://www.iso.org/standard/79106.html
  2. Common Encryption (CENC) in ISO base media file format files — ISO/IEC 23001-7. ISO/IEC. Defines the cenc (AES-CTR) and cbcs (AES-CBC) schemes; the cbcs scheme lets one encrypted package serve Widevine, PlayReady, and FairPlay. Tier 1 (primary standard). https://www.iso.org/standard/68042.html
  3. HTTP Live Streaming — IETF RFC 8216. IETF, 2017. The HLS playlist format that Apple's AVPlayer consumes natively on iOS and tvOS. Tier 1 (primary standard). https://www.rfc-editor.org/rfc/rfc8216
  4. Dynamic adaptive streaming over HTTP (MPEG-DASH) — ISO/IEC 23009-1. ISO/IEC. The DASH manifest consumed by ExoPlayer, Shaka Player, and dash.js on non-Apple platforms. Tier 1 (primary standard). https://www.iso.org/standard/83314.html
  5. Media Source Extensions™ — W3C Recommendation. W3C, 17 November 2016. The browser API that lets JavaScript players (Shaka, hls.js, dash.js) feed adaptive media to the <video> element. Tier 1 (primary standard). https://www.w3.org/TR/media-source/
  6. Encrypted Media Extensions — W3C Recommendation. W3C, 18 September 2017. The browser API through which one web codebase drives Widevine, PlayReady, or FairPlay; support and key-system availability vary by browser. Tier 1 (primary standard). https://www.w3.org/TR/encrypted-media/
  7. Media3 ExoPlayer. Google, Android Developers. The native Android player engine (in Jetpack Media3) for DASH/HLS playback and Widevine, shared across Android phones, Android TV, and Fire TV. Tier 3 (first-party). https://developer.android.com/media/media3/exoplayer
  8. AVPlayer / AVFoundation. Apple Developer. The native iOS and tvOS player for HLS and FairPlay; one Swift player serves iPhone, iPad, and Apple TV. Tier 3 (first-party). https://developer.apple.com/documentation/avfoundation/avplayer
  9. AVPlay API and DRM (PlayReady, Widevine). Samsung Developer (Tizen Smart TV). The native media interface used inside an HTML Tizen app for adaptive streaming, 4K, and DRM. Tier 3 (first-party). https://developer.samsung.com/smarttv/develop/api-references/samsung-product-api-references/avplay-api.html
  10. SceneGraph and the Video node. Roku Developer. Roku's BrightScript-based UI framework and native video component — a separate platform and language from web and mobile. Tier 3 (first-party). https://developer.roku.com/docs/developer-program/core-concepts/core-concepts.md
  11. Roku and Samsung dominate connected-TV platforms in the US. Parks Associates, April 2026. US CTV platform share: Roku ≈28%, Samsung Tizen ≈23%, with Fire TV, LG webOS, and Vizio mid-tier — the reason the TV platforms cannot be skipped. Tier 6 (industry reference for current numbers). https://www.parksassociates.com/blogs/pr-video-services-ott-pay-tv/roku-and-samsung-dominate-connected-tv-platforms
  12. The recipe for a future-proof OTT video player. IABM. Wrapper-around-native-players vs unified-core player architectures and the application-level code needed to manage per-platform differences. Tier 5 (industry). https://theiabm.org/news/the-recipe-for-a-future-proof-ott-video-player/

Where sources disagreed, the official standard was followed. The popular "one player runs on every device" framing was overridden by the W3C MSE/EME scope (a web player is browser-only) and by each platform's native engine + DRM pairing. The "cross-platform framework = unified video" framing was overridden by the first-party reality that React Native and Flutter bridge to the same native AVPlayer/ExoPlayer and do not target Roku, Tizen, or webOS.