Back to Knowledge Hub
Fingerprint

MIME and Codec Fingerprinting: Media Support Signals

How canPlayType, isTypeSupported, and MediaCapabilities report media support, why results vary by host, and how to check them against one profile.

BotBrowser Team

Documentation

Prefer the maintained product doc?

This article has a matching page in the docs center. Use the docs for the canonical setup flow, current flags, and long-term reference.

BotBrowser can report MIME and codec support from the loaded profile, but it cannot add decoders that the host does not have or guarantee that a site will accept every reported capability. Before a page loads a video, it can ask the browser which media types it can handle. Three interfaces answer that question: canPlayType() on media elements, MediaSource.isTypeSupported() for Media Source Extensions, and MediaCapabilities.decodingInfo() for decoding. The answers exist to help a page choose a format, but they also describe the machine that produced them. A reader who understands what each interface returns, and why the result differs between systems, can review a browser setup without guessing.

Three media APIs, canPlayType, isTypeSupported, and decodingInfo, are answered from the loaded profile and the host decoders, then compared across the main page, Workers, and iframes.

What the three media APIs report

HTMLMediaElement.canPlayType(type) takes a MIME type string, optionally with a codecs parameter, and returns an empty string, maybe, or probably (MDN canPlayType). An empty string means the browser cannot play that type. probably means it is confident, and maybe means it cannot tell without trying. The three-value answer is deliberately coarse, so it says little about quality or performance.

MediaSource.isTypeSupported(type) is a static method that returns a boolean for a MIME type with an optional codecs list (MDN isTypeSupported). It tells a player whether the browser is likely to accept that type for streaming through Media Source Extensions. A true result is a prediction about support, not proof that a particular stream will play.

MediaCapabilities.decodingInfo(configuration) is the most detailed of the three (MDN decodingInfo). The configuration describes a stream, including a content type and, for video, the dimensions, bitrate, and frame rate. The returned promise resolves with supported, smooth, and powerEfficient flags. The last two describe how well decoding is expected to go, so they depend more on the hardware and on the browser's own history than the plain support answers do.

All three start from a MIME type string. A bare container type such as video/mp4 says little, because one container can carry many codecs. The optional codecs parameter names the actual codecs, for example avc1.42E01E for H.264 video, mp4a.40.2 for AAC audio, vp09.00.10.08 for VP9 video, or opus for Opus audio. Codec strings are compact on purpose and encode a profile and level, so two strings for the same codec family can receive different answers.

The type field of a decodingInfo() configuration matters as well. file describes plain playback of a resource, while media-source describes streaming through Media Source Extensions, and the two paths do not have to agree. Asking with the type that matches how your player really works keeps the answer relevant to the job, and it keeps the question the same each time you ask it.

The calls below show the three shapes side by side.

const type = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
document.createElement('video').canPlayType(type);
MediaSource.isTypeSupported(type);
await navigator.mediaCapabilities.decodingInfo({
  type: 'media-source',
  video: { contentType: 'video/mp4; codecs="avc1.42E01E"', width: 1280, height: 720, bitrate: 2500000, framerate: 30 },
});

The same code can print different answers on two computers, or on one computer after a browser update. The MIME string is only a question. The answer comes from the decoders and policies available to the browser that receives it.

Encrypted media adds another layer. decodingInfo() can also be asked about a key system, which belongs to DRM rather than plain codec support. DRM fingerprinting covers that boundary, and WebCodecs capabilities covers the lower-level codec interface, which has its own support queries.

Why answers differ between machines

A media answer is the result of several layers, and each layer can change independently. The browser build decides which codecs are compiled in or licensed, and some open builds ship without proprietary codecs. The operating system decides which decoders are reachable through its media services. The graphics hardware decides which codecs can be decoded in hardware. Policy and settings can turn features off on a managed machine.

  • Browser release: new codecs and container features arrive over time, and a code path can lose support for an old one.
  • Operating system: a decoder installed on one system may be absent from another, even for the same browser version.
  • Hardware: newer graphics hardware can decode codecs that an older device handles in software or not at all.
  • Configuration: enterprise policy, user settings, and command-line flags can change what the browser reports.

Examples make the pattern concrete. H.264 video and AAC audio are widely available on mainstream desktop browsers, and VP9 and Opus are also common. HEVC often depends on a decoder that the operating system or graphics hardware provides, and AV1 depends on either a software decoder in the browser or newer hardware. These are tendencies, not rules, and a given system can sit anywhere in between.

The environment you test in changes the picture as well. A virtual machine or a remote desktop session often lacks hardware decoding, so powerEfficient results and high-resolution answers there can differ a great deal from those on a physical desktop of the same operating system. When you compare two hosts, note whether each one is physical or virtual, because that single fact explains many differences that look mysterious at first.

This variation is why the answers are useful for compatibility and also why they carry information about the host. A site that asks about a dozen codec strings receives a pattern, and the pattern tends to cluster by operating system family and hardware generation. That is the whole reason media queries appear in privacy discussions.

It is worth being precise about the weight of that pattern. A media support vector is one signal among many. It does not identify a person, no single result decides anything, and many different people share the same pattern. Its practical effect is to narrow the set of devices that a combined set of signals could describe, which is why it should agree with the other signals a browser reports.

The answer can also change on the same machine. A browser update can add a decoder path, a driver update can enable hardware decoding, and a policy change can turn a feature off. A recorded answer is therefore a dated observation of one setup, and it should be rechecked after updates instead of being treated as a permanent property.

Reading the answers side by side

The three interfaces are related but not interchangeable, and a useful review compares them using the same codec strings. Each one has a different job.

  • canPlayType() answers for plain playback in a media element and returns a three-state string.
  • isTypeSupported() answers for streaming through Media Source Extensions and returns a boolean.
  • decodingInfo() answers for a described stream and adds smooth and powerEfficient.

Two consistency expectations follow. First, the answers should agree in direction: if decodingInfo() reports supported as false for a codec at a modest resolution, isTypeSupported() returning true for the same type needs an explanation. Second, a codec family should look coherent for the platform. A report that lists very recent hardware-only codecs next to a missing baseline codec such as H.264 is unusual for a mainstream desktop browser, and unusual is itself informative.

Spelling and ordering matter too. The codecs parameter can be written several ways, for example with different profile and level codes for the same codec family, and a browser can answer differently for each one. Compare strings exactly as a page would send them, and keep the list you test fixed between runs, so that a change in the answer reflects a change in the browser and not a change in the question.

smooth and powerEfficient deserve extra care. They describe expected decoding quality and power use, so they can vary with the hardware and with the browser's playback history. Treat them as softer evidence than supported, and do not expect two runs on two different hosts to match them.

Sequence matters in practice. canPlayType() and isTypeSupported() answer immediately, while decodingInfo() returns a promise, so a script has to wait for each result before it records anything. Run every call from one code path, collect the answers in a fixed order, and compare against a baseline you produced yourself on the same host. A list copied from a web page or from another machine describes a different environment, and it will mislead a review more often than it helps.

Here is a typical reading. Suppose canPlayType() returns probably for H.264 and AAC in MP4, isTypeSupported() returns true for the same type, and decodingInfo() reports supported and smooth for a 720p stream. Those three agree, and the host plainly handles baseline web video. If decodingInfo() for a high-resolution AV1 stream reports supported but not powerEfficient, that can point to software decoding rather than a contradiction.

Checking one profile across contexts

Media APIs are not only available on the main page. navigator.mediaCapabilities can be reached from Workers, and media elements and Media Source Extensions can be used inside iframes. A page can therefore ask the same question from several contexts, and a browser setup that reports one answer on the main page and another in a Worker or frame looks inconsistent even when each answer is plausible alone.

For a consistency review, this means the check should run in more than one place. Ask the same fixed list of types from the main page, from a dedicated Worker where the API is available, and from a same-origin iframe, then compare the results line by line. Differences point to a setup problem. Agreement does not prove the setup is good, but disagreement is a clear sign that something is off.

If your workflow embeds third-party players, include a cross-origin iframe in the comparison as well. Embedded players tend to ask these questions on their own, often at load time, so the answer they receive is the one that decides which stream they request. A same-origin test frame is easier to run, but it does not replace a check in the frame your workflow really uses.

  1. Fix a list of type strings that matches the media your workflow actually uses, with explicit codecs parameters.
  2. Run the three APIs against the list on the main page and record each answer.
  3. Repeat from a dedicated Worker and from a same-origin iframe, where the API is available there.
  4. Compare line by line, then compare the result with what the loaded profile is meant to describe.
  5. Play a short sample of each important type to see what the host really decodes.

A mismatch is a finding to explain, not noise to ignore. Common causes are a setup that was changed in one context only, a list that mixes strings with different profile and level codes, and an answer that reflects the host's real decoders instead of the profile. Fix the cause, then rerun the whole list, because a partial rerun can hide a new difference. For a broader method of comparing a browser across platforms, see evaluating cross-platform browser consistency.

A short written record makes the result repeatable. Note the profile or configuration loaded, the browser version, the host operating system and graphics hardware, the list of types tested, and the answers by context. Add one line about real playback: whether a sample of each important type actually played on the host. Support answers describe what the browser reports, while playback shows what the host can decode, and the two are separate facts.

Privacy limits of media queries

Media queries are easy to run, need no permission prompt, and return quickly, which is why they are common in compatibility code and also why they are discussed as a fingerprinting surface. Ordinary compatibility use is legitimate, because a video site has to know which format to send.

Reduce exposure the same way you would for any capability query. Ask only about the types the page needs to play, prefer a single decisive question over a sweep of every codec string, and avoid storing complete answers next to account or device identifiers. If a support report needs capability data, record the specific failing type and the browser version instead of a full inventory.

Do not present media support as a way to recognize a particular person. It is a coarse hardware and software indicator that many users share. Describing it accurately helps readers decide what is worth protecting and what is only background detail.

Keep related signals in view as well. Codec answers should agree with the operating system, the graphics information, and the DRM capabilities that the same browser reports. A media report that fits the platform but contradicts another surface is still inconsistent, so review them together instead of one at a time.

Teams that keep a release checklist can add a single line for media support. Record the fixed list of types, the answers from each context, and the date, and rerun the list after a browser update or a change to the profile. A small recurring check catches a drifting answer early, and it produces a record that a colleague can read without needing to repeat the investigation.

Choosing a media types mode

When a profile is loaded, the --bot-media-types flag decides how the profile's media types relate to the host's own decoders. It accepts three documented values (MIME and codec documentation).

  • expand is the default. It prioritizes local decoders and extends the list with the types the profile defines, so the result mixes the host's real capability with the profile's.
  • profile uses only the media types the profile defines. It does not mix in host decoders, so it fits a consistency check that compares answers with the loaded profile.
  • real uses the actual system media types. It fits checking what the host can really decode, but it may not match a profile that describes different hardware.

Pick the mode from the question you are asking. For a consistency review, start with profile and compare the three APIs across contexts. Move to expand when playback on the host matters more than a strict match, and use real to learn what the host itself reports. Change one setting at a time and keep the list of types fixed between runs.

Consider a profile that describes a desktop system and a host that lacks a decoder for one codec the profile lists. In profile mode the APIs report the profile's list, which is what a consistency check wants to see, while real playback of that codec on this host would still fail. In real mode the answers follow the host and would reveal the missing decoder. Neither mode is wrong. They answer different questions, and the review record should say which question was asked.

Mode behavior can change between releases, so reread the documentation before relying on a detail from this page, and rerun your fixed list after updating either the browser or the profile. Compare the new record with the previous one. A difference that appears right after an update is much easier to explain than one discovered months later.

BotBrowser reports MIME type and codec support through the loaded profile and provides --bot-media-types with the modes expand (the default), profile, and real, so canPlayType(), isTypeSupported(), and MediaCapabilities results can be checked for consistency across the main page, Workers, Service Workers, and iframes. That gives you a documented way to confirm that the media answers agree with one profile. BotBrowser cannot add decoders the host lacks, cannot guarantee that a given site treats the reported media capabilities as acceptable, and does not replace checking real playback on the target workflow.

Public sources

#Mime#Codec#Media#Fingerprinting#Privacy#Mediacapabilities

Take BotBrowser from research to production

The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.