Skip to content

Repository files navigation

Community Project header

New Relic Video Agent for Android

License

The New Relic Video Agent for Android provides comprehensive video analytics for Android applications using ExoPlayer (Media3) or THEOplayer (Dolby OptiView). Track video events, monitor playback quality, identify errors, and gain deep insights into user engagement and performance — for both mobile and Android TV.

Features

  • Automatic Event Detection — Captures ExoPlayer and THEOplayer lifecycle events automatically without manual instrumentation
  • Multi-Player SDK Support — Plug-in tracker modules for ExoPlayer (Media3) and THEOplayer (Dolby OptiView); swap or run both in the same app
  • QoE Metrics — Quality of Experience aggregation for startup time, buffering ratio, bitrate, download throughput, rendition switches, pause time, and playback errors
  • Event Segregation — Organized event types: VideoAction, VideoAdAction, VideoErrorAction, VideoCustomAction
  • IMA Ads Support — Built-in Google IMA SDK ad tracking via dedicated ad tracker
  • Android TV Support — Auto-detection of Android TV with optimized harvest cycles
  • Multi-Player Support — Track multiple simultaneous video players in the same application
  • Easy Integration — JitPack dependency or manual AAR/source import

Table of Contents

Installation

Option 1: Install via JitPack (Recommended)

Add the JitPack repository inside your root build.gradle:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Add the dependencies inside your app's build.gradle:

dependencies {
    // Required: Core library
    implementation 'com.github.newrelic.video-agent-android:NewRelicVideoCore:v5.0.0'

    // ExoPlayer (Media3) tracker — choose one player tracker
    implementation 'com.github.newrelic.video-agent-android:NRExoPlayerTracker:v5.0.0'

    // THEOplayer (Dolby OptiView) tracker — alternative to NRExoPlayerTracker
    implementation 'com.github.newrelic.video-agent-android:NRTHEOPlayerTracker:v5.0.0'

    // Google IMA ad tracker (optional — for client-side ad insertion, ExoPlayer only)
    implementation 'com.github.newrelic.video-agent-android:NRIMATracker:v5.0.0'

    // AWS MediaTailor ad tracker (optional — for server-side ad insertion / SSAI)
    implementation 'com.github.newrelic.video-agent-android:NRMediaTailorTracker:v5.0.0'
}

Note: All modules are versioned together — always use the same version number for every module you include. See releases for the latest version. Use either NRExoPlayerTracker or NRTHEOPlayerTracker — both can coexist in the same app for multi-player scenarios. NRIMATracker and NRMediaTailorTracker are mutually exclusive per player — pick one based on whether your stream uses CSAI (IMA) or SSAI (MediaTailor).

Option 2: Install Manually Using AAR Files

  1. Clone this repo.
  2. Open it with Android Studio.
  3. Click on View > Tool Windows > Gradle to open the Gradle tool window.
  4. Unfold NRVideoProject > Tasks > build and double-click assemble. This generates AAR files inside each module's build/outputs/aar/ directory.
  5. In your project, click File > New > New Module > Import .JAR/.AAR Package and click Next.
  6. Select the generated AAR file and click Finish.
  7. Repeat steps 5–6 for each module you need.
  8. Add the dependencies in your app's build.gradle:
dependencies {
    implementation project(":NewRelicVideoCore")
    implementation project(":NRExoPlayerTracker")    // or NRTHEOPlayerTracker
    implementation project(":NRIMATracker")           // optional — CSAI ads
    implementation project(":NRMediaTailorTracker")   // optional — SSAI ads
}

Option 3: Install Manually Using Source Code

  1. Clone this repo.
  2. In your project, click File > New > Import Module.
  3. Select the module directory and click Finish.
  4. Repeat for each module you need.
  5. Add the dependencies in your app's build.gradle:
dependencies {
    implementation project(":NewRelicVideoCore")
    implementation project(":NRExoPlayerTracker")    // or NRTHEOPlayerTracker
    implementation project(":NRIMATracker")           // optional — CSAI ads
    implementation project(":NRMediaTailorTracker")   // optional — SSAI ads
}

Prerequisites

Before using the Video Agent, ensure you have:

  • New Relic Account — Active account with a valid application token
  • New Relic Android Agent — Installed and configured in your project
  • ExoPlayer / Media3 (if using NRExoPlayerTracker) — androidx.media3:media3-exoplayer:1.2.0 or later
  • THEOplayer SDK (if using NRTHEOPlayerTracker) — com.theoplayer.theoplayer-sdk-android:core:11.x
  • Google IMA SDK (optional) — androidx.media3:media3-exoplayer-ima:1.2.0 if tracking CSAI ads with ExoPlayer
  • AWS MediaTailor (optional) — No extra SDK needed; requires a valid MediaTailor session tracking URL
  • Android minSdk — API 24 (Android 7.0) or higher

Modules

The Video Agent is composed of five modules:

Module Description Required
NewRelicVideoCore Base classes for tracker management, event generation, and data harvesting. Depends on the New Relic Android Agent. Yes
NRExoPlayerTracker Video tracker for ExoPlayer (Media3). Automatically hooks into player lifecycle events. Yes (for ExoPlayer)
NRTHEOPlayerTracker Video tracker for THEOplayer (Dolby OptiView). Hooks into THEOplayer event listeners for lifecycle, QoE, rendition changes, and DRM errors. Yes (for THEOplayer)
NRIMATracker Ad tracker for the Google IMA SDK (client-side ad insertion / CSAI). Captures ad lifecycle events including quartiles, breaks, and errors. Optional
NRMediaTailorTracker Ad tracker for AWS Elemental MediaTailor (server-side ad insertion / SSAI). Supports DASH and HLS, explicit and implicit session init, live + VOD, with rich VAST metadata. Optional

Usage

Getting Your Application Token

Before initializing the Video Agent, obtain your application token:

  1. Log in to one.newrelic.com
  2. Navigate to the Streaming Video & Ads onboarding flow
  3. Copy your applicationToken

Basic Setup — ExoPlayer Only

// Step 1: Initialize NRVideo in your main activity (e.g., MainActivity.java)
NRVideoConfiguration config = new NRVideoConfiguration.Builder("YOUR_APPLICATION_TOKEN")
        .autoDetectPlatform(getApplicationContext())
        .withHarvestCycle(5 * 60) // 300 seconds (5 minutes) — recommended for on-demand video
        .build();

NRVideo.newBuilder(getApplicationContext())
        .withConfiguration(config)
        .build();

// Step 2: Initialize the player and register (e.g., VideoPlayer.java)
ExoPlayer player = new ExoPlayer.Builder(this).build();

Map<String, Object> customAttrs = new HashMap<>();
customAttrs.put("contentTitle", "My Video Title");

NRVideoPlayerConfiguration playerConfig =
        new NRVideoPlayerConfiguration("my-player", player,
                NRVideoPlayerConfiguration.PLAYER_TYPE_EXO, null, customAttrs);

Integer trackerId = NRVideo.addPlayer(playerConfig);

// Step 3 (Optional): Release the tracker when done
@Override
protected void onDestroy() {
    NRVideo.releaseTracker(trackerId);
    player.release();
    super.onDestroy();
}

Basic Setup — THEOplayer

Prerequisite: Your AndroidManifest.xml must include a valid THEOplayer license key:

<meta-data android:name="THEOPLAYER_LICENSE" android:value="${theoplayerLicenseKey}" />
// Step 1: Initialize NRVideo in your Application class or main activity
NRVideoConfiguration config = new NRVideoConfiguration.Builder("YOUR_APPLICATION_TOKEN")
        .autoDetectPlatform(getApplicationContext())
        .withHarvestCycle(5 * 60) // 300 seconds (5 minutes) — recommended for on-demand video
        .build();

NRVideo.newBuilder(getApplicationContext())
        .withConfiguration(config)
        .build();

// Step 2: Register the THEOplayerView
THEOplayerView theoPlayerView = findViewById(R.id.theo_player_view);

Integer trackerId = NRVideo.addPlayer(
        new NRVideoPlayerConfiguration("theo-player", theoPlayerView,
                NRVideoPlayerConfiguration.PLAYER_TYPE_THEO, null, null));

// Step 3: Forward Activity lifecycle events
@Override protected void onResume()  { super.onResume();  theoPlayerView.onResume(); }
@Override protected void onPause()   { super.onPause();   theoPlayerView.onPause(); }
@Override protected void onDestroy() {
    super.onDestroy();
    NRTracker tracker = NewRelicVideoAgent.getInstance().getContentTracker(trackerId);
    if (tracker instanceof NRTrackerTHEOPlayer) {
        ((NRTrackerTHEOPlayer) tracker).onDestroy();
    }
    NRVideo.releaseTracker(trackerId);
}

Note: NRTrackerTHEOPlayer.onDestroy() must be called before NRVideo.releaseTracker() to ensure THEOplayerView.onDestroy() is forwarded and listeners are cleanly unregistered.

Setup with ExoPlayer and AWS MediaTailor (SSAI)

Pass NRAdConfig.mediaTailor() (or one of its overloads) as the third argument to NRVideoPlayerConfiguration. The tracker activates automatically when addPlayer is called.

// Step 1: Initialize NRVideo (same as basic setup)

// Step 2: POST to /v1/session/… to create a session.
//   POST  https://<hash>.mediatailor.<region>.amazonaws.com/v1/session/<hash>/<config>/<manifest>
//   body  {"reportingMode":"server", "adsParams":{…targeting params…}}
//   resp  {"manifestUrl":"/v1/dash/…?aws.sessionId=…", "trackingUrl":"/v1/tracking/…"}
// Both returned paths are root-relative — prepend your MediaTailor host.

String manifestUrl = /* absolute manifest URL from session-init response */;
String trackingUrl = /* absolute tracking URL from session-init response */;

ExoPlayer player = new ExoPlayer.Builder(this).build();

// Step 3: Register the player with NRAdConfig.mediaTailor().
//   Pass the trackingUrl from session init directly — no extra wiring needed.
NRVideoPlayerConfiguration playerConfig = new NRVideoPlayerConfiguration(
        "mediatailor-player",
        player,
        NRVideoPlayerConfiguration.PLAYER_TYPE_EXO,
        NRAdConfig.mediaTailor(null, trackingUrl),
        /* custom attrs */ null);
Integer trackerId = NRVideo.addPlayer(playerConfig);

// Step 4: Hand ExoPlayer the manifest URL and start playback.
player.setMediaItem(MediaItem.fromUri(manifestUrl));
player.prepare();
player.setPlayWhenReady(true);

// Step 5 (Optional): For a "Skip ad" button in your UI.
// NRTrackerMediaTailor adTracker = (NRTrackerMediaTailor)
//         NewRelicVideoAgent.getInstance().getAdTracker(trackerId);
// adTracker.notifyAdSkipped();

Custom CDN

When MediaTailor serves ad segments from a customer-owned CDN domain, ad segments have no segments.mediatailor in their URLs. Pass your CDN's ad-segment path prefix so the tracker can detect them. The AWS-recommended prefix /tm/ is already checked automatically — only set segmentPrefix if your CDN uses a different path.

// /tm/ is checked automatically — no segmentPrefix needed for the common case
NRAdConfig.mediaTailor()

// Custom CDN with /tm/ ad-segment path — still automatic, no override needed
NRAdConfig.mediaTailor()

// Custom CDN with a non-/tm/ ad-segment path
NRAdConfig.mediaTailor("/my-ads/")

// Custom CDN + explicit tracking URL (POST session-init flow)
NRAdConfig.mediaTailor("/my-ads/", trackingUrl)

What the tracker emits on VideoAdAction: AD_BREAK_START, AD_REQUEST, AD_START, AD_PAUSE, AD_RESUME, AD_SEEK_START, AD_SEEK_END, AD_BUFFER_START, AD_BUFFER_END, AD_QUARTILE (25/50/75%), AD_END, AD_BREAK_END, AD_SKIP, AD_ERROR. All events carry trackerName="NRMTracker", adPartner="aws-mediatailor", plus rich VAST metadata (see DATAMODEL.md).

Setup with ExoPlayer and IMA Ads

// Step 1: Initialize NRVideo (same as above)

// Step 2: Build the player with IMA ad support
ExoPlayer player = new ExoPlayer.Builder(this)
        .setMediaSourceFactory(mediaSourceFactory)
        .build();

NRVideoPlayerConfiguration playerConfig =
        new NRVideoPlayerConfiguration("my-player", player,
                NRVideoPlayerConfiguration.PLAYER_TYPE_EXO, NRAdConfig.csai(), null);

Integer trackerId = NRVideo.addPlayer(playerConfig);

// Step 3: Wire up the IMA ad tracker
NRTrackerIMA adTracker =
        (NRTrackerIMA) NewRelicVideoAgent.getInstance().getAdTracker(trackerId);

ImaAdsLoader.Builder builder = new ImaAdsLoader.Builder(this);
builder.setAdErrorListener(adTracker.getAdErrorListener());
builder.setAdEventListener(adTracker.getAdEventListener());

// Step 4 (Optional): Release on destroy
@Override
protected void onDestroy() {
    NRVideo.releaseTracker(trackerId);
    player.release();
    super.onDestroy();
}

Best Practices

1. Setting contentTitle

The contentTitle attribute displays a value if your video metadata contains title information. For best results, explicitly set it during player configuration:

Map<String, Object> customAttrs = new HashMap<>();
customAttrs.put("contentTitle", "My Video Title");

NRVideoPlayerConfiguration playerConfig =
        new NRVideoPlayerConfiguration("my-player", player, false, customAttrs);

2. Setting userId

Set a user identifier to track video analytics per user:

// Set userId globally across all trackers
NRVideo.setUserId("user-12345");

3. Adding Custom Attributes

Add custom attributes to improve data aggregation and analysis:

Map<String, Object> customAttrs = new HashMap<>();
customAttrs.put("contentTitle", videoMetadata.getTitle());
customAttrs.put("subscriptionTier", "premium");
customAttrs.put("contentProvider", "studio-abc");
customAttrs.put("region", "us-west-2");
customAttrs.put("cdnProvider", "cloudflare");

NRVideoPlayerConfiguration playerConfig =
        new NRVideoPlayerConfiguration("my-player", player, false, customAttrs);

You can also set attributes after initialization:

// Set attribute on a specific content tracker
NRVideo.setAttribute(trackerId, "contentSeries", "Season 1");

// Set attribute on ad tracker
NRVideo.setAdAttribute(trackerId, "adCampaign", "spring-promo");

// Set global attribute across all trackers
NRVideo.setGlobalAttribute("appVersion", "2.1.0");

Use these attributes in New Relic queries:

-- Analyze by subscription tier
SELECT count(*) FROM VideoAction WHERE actionName = 'CONTENT_START'
FACET subscriptionTier SINCE 1 day ago

-- Monitor by region
SELECT average(contentPlayhead) FROM VideoAction
FACET region SINCE 1 hour ago

4. Gradual Rollout with Feature Flags

When deploying to production, use feature flags to enable the tracker gradually:

int rolloutPercentage = 5; // Start with 5% of users

boolean shouldEnable = (userId.hashCode() % 100) < rolloutPercentage;

if (shouldEnable) {
    NRVideoConfiguration config = new NRVideoConfiguration.Builder("YOUR_APPLICATION_TOKEN")
            .autoDetectPlatform(getApplicationContext())
            .withHarvestCycle(5 * 60)
            .build();
    NRVideo.newBuilder(getApplicationContext()).withConfiguration(config).build();
}

Recommended Rollout Schedule:

Phase Percentage Duration Validation
Initial 5% 2–3 days Verify data flowing to New Relic
Early 15% 3–5 days Check data quality and performance
Expansion 25% 5–7 days Validate across device types
Majority 50% 1–2 weeks Monitor at scale
Full 100% Ongoing Complete deployment

Configuration Options

NRVideoConfiguration

Builder Method Type Default Description
Builder(applicationToken) String — Required. Your New Relic application token. Used for authentication and region detection.
.autoDetectPlatform(context) Context Mobile Auto-detect Mobile vs. Android TV platform.
.withHarvestCycle(seconds) int 300 (Mobile) / 180 (TV) Interval in seconds between data harvests. For on-demand video, use a minimum of 300 seconds.
.enableLogging() — Disabled Enable debug logging for development.
.enableQoeAggregate(enabled) boolean true Enable or disable Quality of Experience event aggregation (QOE_AGGREGATE events). QoE is enabled by default — call .enableQoeAggregate(false) to opt out.
.withQoeAggregateIntervalMultiplier(multiplier) int 2 Controls how often QOE_AGGREGATE events are emitted relative to the harvest cycle. 1 = every harvest cycle, 2 = every other cycle, 3 = every third, etc. The first and last harvest cycles always emit a QOE_AGGREGATE event regardless of this value. Call .withQoeAggregateIntervalMultiplier(n) to change the frequency.
.withMemoryOptimization() — Disabled Optimize for low-memory devices.

NRVideoPlayerConfiguration

Parameter Type Description
playerName String Unique identifier for the video player. Used to distinguish between multiple players.
player ExoPlayer / THEOplayerView The player instance to track.
playerType String NRVideoPlayerConfiguration.PLAYER_TYPE_EXO ("exo") or PLAYER_TYPE_THEO ("theo"). Always set this explicitly — omitting it triggers a warning log and falls back to ExoPlayer for backward compatibility only.
adConfig NRAdConfig Ad framework configuration. Pass NRAdConfig.csai() for IMA, NRAdConfig.mediaTailor() for MediaTailor, or null for no ad tracking.
customAttributes Map<String, Object> Custom attributes to attach to all events from this player.

Upgrading from v4.2.0? The old boolean isAdEnabled and AdTrackerType constructors are still supported but deprecated. They compile without changes — true maps to NRAdConfig.csai(), AdTrackerType.IMA maps to NRAdConfig.csai(), AdTrackerType.MEDIA_TAILOR maps to NRAdConfig.mediaTailor(). Migrate at your own pace.

Custom Attribute Limits

Limits for custom attributes added to default mobile events:

  • Attributes: 128 maximum
  • String attributes: 4 KB maximum length (empty string values are not accepted)

Note: There are special keywords reserved for default attributes documented in DATAMODEL.md. Please do not use these as custom attribute names, as they will be dropped by the agent.

Live Stream Configuration

The agent uses a separate harvest interval for VOD and LIVE content:

Content Type Default Interval Range Builder Method
VOD 300s (Mobile) / 180s (TV) 5–300s .withHarvestCycle(seconds)
LIVE 30s (Mobile) / 10s (TV) 1–60s .withLiveHarvestCycle(seconds)
// Live stream — flush every 10 seconds on TV, 30s on Mobile (or override)
NewRelicVideoAgent tracker = new NewRelicVideoAgent.Builder()
    .withHarvestCycle(300)
    .withLiveHarvestCycle(10)
    .build();

Note: Harvest intervals are immutable after .build().

API Reference

NRVideo (Primary API)

NRVideo.addPlayer(playerConfig)

Register a player with the Video Agent. Returns a trackerId for future reference.

Integer trackerId = NRVideo.addPlayer(playerConfig);

NRVideo.releaseTracker(trackerId)

Release a tracker when the player is destroyed.

NRVideo.releaseTracker(trackerId);

NRVideo.setUserId(userId)

Set a unique identifier for the current user across all trackers.

NRVideo.setUserId("user-12345");

NRVideo.setAttribute(trackerId, key, value)

Set a custom attribute on a specific content tracker.

NRVideo.setAttribute(trackerId, "contentSeries", "Season 1");

NRVideo.setGlobalAttribute(key, value)

Set a custom attribute across all active trackers.

NRVideo.setGlobalAttribute("appVersion", "2.1.0");

NRVideo.recordCustomEvent(attributes)

Record a custom event across all trackers.

Map<String, Object> attrs = new HashMap<>();
attrs.put("actionName", "VideoBookmarked");
attrs.put("bookmarkPosition", player.getCurrentPosition());
NRVideo.recordCustomEvent(attrs);

NRVideo.recordCustomEvent(attributes, trackerId)

Record a custom event on a specific tracker. Requires an actionName key.

Map<String, Object> attrs = new HashMap<>();
attrs.put("actionName", "QualityChanged");
attrs.put("newQuality", "1080p");
NRVideo.recordCustomEvent(attrs, trackerId);

NRTrackerExoPlayer (ExoPlayer Tracker)

tracker.setDroppedFrameAggregationEnabled(enabled)

Enable or disable dropped frame aggregation (5-second sliding window, max 50 ExoPlayer callbacks per window). Enabled by default.

NRTrackerExoPlayer tracker =
        (NRTrackerExoPlayer) NewRelicVideoAgent.getInstance().getContentTracker(trackerId);
tracker.setDroppedFrameAggregationEnabled(true);

For a full description of CONTENT_DROPPED_FRAMES event fields and their semantics, see DATAMODEL.md.

Example: Complete Integration

// --- MainActivity.java ---
NRVideoConfiguration config = new NRVideoConfiguration.Builder("YOUR_APPLICATION_TOKEN")
        .autoDetectPlatform(getApplicationContext())
        .withHarvestCycle(5 * 60)
        .enableLogging()           // Enable for development
        // QoE is enabled by default; call .enableQoeAggregate(false) to disable
        .build();

NRVideo.newBuilder(getApplicationContext())
        .withConfiguration(config)
        .build();

NRVideo.setUserId("user-12345");

// --- VideoPlayer.java ---
ExoPlayer player = new ExoPlayer.Builder(this).build();

Map<String, Object> customAttrs = new HashMap<>();
customAttrs.put("contentTitle", "Big Buck Bunny");
customAttrs.put("contentProvider", "studio-abc");

NRVideoPlayerConfiguration playerConfig =
        new NRVideoPlayerConfiguration("main-player", player,
                NRVideoPlayerConfiguration.PLAYER_TYPE_EXO, null, customAttrs);

Integer trackerId = NRVideo.addPlayer(playerConfig);

// Cleanup
@Override
protected void onDestroy() {
    NRVideo.releaseTracker(trackerId);
    player.release();
    super.onDestroy();
}

Data Model

The Video Agent captures comprehensive video analytics across four event types:

  • VideoAction — Playback lifecycle events (request, start, pause, resume, buffer, seek, rendition changes, heartbeats)
  • VideoAdAction — Ad lifecycle events (request, start, end, quartiles, breaks, clicks)
  • VideoErrorAction — Error events (playback failures, ad errors, crashes)
  • VideoCustomAction — Custom events defined by your application

Full Documentation: See DATAMODEL.md for the complete event and attribute reference, and Advanced Topics for creating custom trackers.

Support

Should you need assistance with New Relic products, you are in good hands with several support channels.

If the issue has been confirmed as a bug or is a feature request, please file a GitHub issue.

Support Channels

Contribute

We encourage your contributions to improve the Video Agent for Android! Keep in mind that when you submit your pull request, you'll need to sign the CLA via the click-through using CLA-Assistant. You only have to sign the CLA one time per project.

If you have any questions, or to execute our corporate CLA (which is required if your contribution is on behalf of a company), drop us an email at opensource@newrelic.com.

For more details on how best to contribute, see CONTRIBUTING.md.

A note about vulnerabilities

As noted in our security policy, New Relic is committed to the privacy and security of our customers and their data. We believe that providing coordinated disclosure by security researchers and engaging with the security community are important means to achieve our security goals.

If you believe you have found a security vulnerability in this project or any of New Relic's products or websites, we welcome and greatly appreciate you reporting it to New Relic through our bug bounty program.

If you would like to contribute to this project, review these guidelines.

To all contributors, we thank you! Without your contribution, this project would not be what it is today.

License

The Video Agent for Android is licensed under the Apache 2.0 License.

About

New Relic Video Agent for Android

Topics

Resources

Code of conduct

Security policy

Stars

5 stars

Watchers

12 watching

Forks

Releases

Packages

Used by

Contributors

Languages