Streamtube

Video Analytics

Mux Data analytics integration guide

Video Analytics Integration

This guide covers how to integrate Mux Data analytics into your video player implementation.

Environment Keys

EnvironmentKey
Productionnl7g58q1rs9lkojadotho5989
Staging0bu16j45b65nhlf6vcqh1f445

Required Metadata

When initializing Mux analytics, you must provide the following metadata:

{
  // Video identification
  video_id: string,              // Unique identifier for the video/stream
  video_title: string,           // Display title of the video
  video_poster_url?: string,     // Thumbnail/poster image URL
  
  // Stream type configuration
  video_stream_type: "live" | "on-demand",
  video_source_is_live: boolean,
  video_content_type?: string,   // e.g., "clip", "vod", "episode"
  
  // Creator & viewer identification
  video_creator_id: string,      // Channel/creator ID
  viewer_user_id: string,        // Your platform's viewer ID
  
  // Third-party integration
  sub_property_id?: string,      // Your internal user ID (for third-party integrations)

  // Custom fields
  custom_1?: string,             // External user ID (optional, for integrators)

  // Page context
  page_type: string,             // e.g., "live", "playlist", "vod"
}

Live Stream Example

const metadata = {
  video_id: String(stream.id),
  video_title: stream.title || `${channel.display_name}'s Stream`,
  video_poster_url: channel.cover_photo_url,
  video_stream_type: "live",
  video_source_is_live: true,
  video_creator_id: String(channel.id),
  viewer_user_id: String(user.id),
  sub_property_id: "YOUR_INTERNAL_USER_ID", // For third-party integrations
  page_type: "live",
};

Clip/VOD Example

When playing clips or VOD content, adjust these fields:

const metadata = {
  video_id: String(clip.id),
  video_title: clip.title,
  video_poster_url: clip.thumbnail_url,
  video_stream_type: "on-demand",        // Changed from "live"
  video_source_is_live: false,           // Changed from true
  video_content_type: "clip",            // Added: specifies content type
  video_creator_id: String(channel.id),
  viewer_user_id: String(user.id),
  page_type: "playlist",
};

Content Type Values

ValueUse Case
clipShort clips, highlights
vodFull video on demand
episodeSeries episodes
movieFull-length movies
trailerPreviews/trailers
eventRecorded events
shortShort-form content

Third-Party Integration

If you're integrating as a third party and need to track your own users:

const metadata = {
  // ... other required fields
  
  // Use sub_property_id for your internal user tracking
  sub_property_id: "your-platform-user-id-12345",
  
  // viewer_user_id is MBG's internal user ID
  viewer_user_id: String(mbgUser.id),
};

SDK Integration Examples

Installation

npm install mux-embed

HTML5 Video Element (Vanilla JavaScript)

<script src="https://src.litix.io/core/4/mux.js"></script>

<video id="my-player" controls>
  <source src="https://example.com/video.mp4" type="video/mp4">
</video>

<script>
  // Capture player init time as early as possible
  const playerInitTime = typeof mux !== 'undefined' ? mux.utils.now() : Date.now();

  // Initialize Mux Data monitoring
  mux.monitor('#my-player', {
    debug: false,
    data: {
      env_key: 'nl7g58q1rs9lkojadotho5989', // Use staging key for non-prod
      
      // Player metadata
      player_name: 'My Video Player',
      player_init_time: playerInitTime,
      
      // Video metadata
      video_id: 'stream-12345',
      video_title: 'My Live Stream',
      video_stream_type: 'live',
      video_source_is_live: true,
      video_creator_id: 'channel-67890',
      
      // Viewer metadata
      viewer_user_id: 'user-abc123',
      sub_property_id: 'your-internal-user-id',

      page_type: 'live',
    }
  });
</script>

React Component

import mux from 'mux-embed';
import React, { useEffect, useRef } from 'react';

export default function VideoPlayer({ stream, channel, user }) {
  const videoRef = useRef(null);

  useEffect(() => {
    if (!videoRef.current) return;

    const playerInitTime = mux.utils.now();

    mux.monitor(videoRef.current, {
      debug: process.env.NODE_ENV === 'development',
      data: {
        env_key: process.env.NEXT_PUBLIC_MUX_ENV_KEY,
        
        // Player metadata
        player_name: 'React Video Player',
        player_init_time: playerInitTime,
        
        // Video metadata
        video_id: String(stream.id),
        video_title: stream.title,
        video_stream_type: 'live',
        video_source_is_live: true,
        video_creator_id: String(channel.id),
        
        // Viewer metadata
        viewer_user_id: String(user.id),

        page_type: 'live',
      }
    });

    // Cleanup on unmount
    return () => {
      if (videoRef.current) {
        mux.emit(videoRef.current, 'destroy');
      }
    };
  }, [stream, channel, user]);

  return (
    <video
      ref={videoRef}
      controls
      src={stream.playback_url}
      style={{ width: '100%' }}
    />
  );
}

With HLS.js

import mux from 'mux-embed';
import Hls from 'hls.js';
import { useEffect, useRef } from 'react';

export default function HLSPlayer({ src, metadata }) {
  const videoRef = useRef(null);
  const hlsRef = useRef(null);

  useEffect(() => {
    const video = videoRef.current;
    if (!video || !src) return;

    const playerInitTime = mux.utils.now();

    if (Hls.isSupported()) {
      hlsRef.current = new Hls();
      hlsRef.current.loadSource(src);
      hlsRef.current.attachMedia(video);
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      video.src = src;
    }

    // Initialize Mux monitoring
    mux.monitor(video, {
      debug: false,
      hlsjs: hlsRef.current, // Pass HLS.js instance for better tracking
      data: {
        env_key: 'nl7g58q1rs9lkojadotho5989',
        player_name: 'HLS Player',
        player_init_time: playerInitTime,
        ...metadata,
      }
    });

    return () => {
      if (hlsRef.current) {
        hlsRef.current.destroy();
      }
      mux.emit(video, 'destroy');
    };
  }, [src, metadata]);

  return <video ref={videoRef} controls style={{ width: '100%' }} />;
}

With NanoPlayer (nanocosmos H5Live)

NanoPlayer may create multiple video elements during ABR (Adaptive Bitrate) switching, especially on iOS. Use the onActiveVideoElementChange event to:

  1. Get the video element when it's created
  2. Listen for video element changes during stream switches

Event Data

PropertyTypeDescription
activeVideoElementHTMLVideoElementThe currently active video element
videoElementListArray<HTMLVideoElement>All video elements (multiple on iOS ABR)

Vanilla JavaScript

var player = new NanoPlayer('playerDiv');
var muxInstance = null;

var config = {
  source: {
    entries: [{
      h5live: {
        rtmp: {
          url: 'rtmp://bintu-play.nanocosmos.de/play',
          streamname: 'XXXXX-YYYYY'
        },
        server: {
          websocket: 'wss://bintu-h5live.nanocosmos.de:443/h5live/stream',
          hls: 'https://bintu-h5live.nanocosmos.de:443/h5live/http/playlist.m3u8'
        }
      }
    }]
  },
  playback: {
    autoplay: true,
    automute: true,
  },
  events: {
    onActiveVideoElementChange: function(event) {
      var activeVideoElement = event.data.activeVideoElement;
      var videoElementList = event.data.videoElementList;
      
      if (!activeVideoElement) return;
      
      // Destroy previous Mux instance if video element changed
      if (muxInstance) {
        mux.emit(muxInstance, 'destroy');
      }
      
      // Store reference and initialize Mux on the new active element
      muxInstance = activeVideoElement;
      var playerInitTime = mux.utils.now();
      
      mux.monitor(activeVideoElement, {
        debug: false,
        data: {
          env_key: 'nl7g58q1rs9lkojadotho5989',
          player_name: 'NanoPlayer',
          player_init_time: playerInitTime,
          
          video_id: 'stream-12345',
          video_title: 'My Live Stream',
          video_stream_type: 'live',
          video_source_is_live: true,
          video_creator_id: 'channel-67890',
          viewer_user_id: 'user-abc123',
          
          page_type: 'live',
        }
      });
      
      console.log('Mux monitoring initialized on:', activeVideoElement.id);
      console.log('Total video elements:', videoElementList.length);
    },
    onDestroy: function() {
      // Cleanup Mux when NanoPlayer is destroyed
      if (muxInstance) {
        mux.emit(muxInstance, 'destroy');
        muxInstance = null;
      }
    }
  }
};

player.setup(config).then(function(config) {
  console.log('NanoPlayer setup complete');
}).catch(function(error) {
  console.error('NanoPlayer setup failed:', error);
});

React Component

import { useEffect, useRef } from 'react';

export default function NanoPlayerWithAnalytics({ streamConfig, metadata }) {
  const playerRef = useRef(null);
  const muxElementRef = useRef(null);

  useEffect(() => {
    if (typeof NanoPlayer === 'undefined') {
      console.error('NanoPlayer not loaded');
      return;
    }

    const player = new NanoPlayer('nanoPlayerDiv');
    playerRef.current = player;

    const config = {
      source: streamConfig.source,
      playback: {
        autoplay: true,
        automute: true,
        ...streamConfig.playback,
      },
      events: {
        onActiveVideoElementChange: (event) => {
          const { activeVideoElement, videoElementList } = event.data;
          
          if (!activeVideoElement) return;
          
          // Cleanup previous Mux instance
          if (muxElementRef.current) {
            mux.emit(muxElementRef.current, 'destroy');
          }
          
          // Initialize Mux on new active element
          muxElementRef.current = activeVideoElement;
          const playerInitTime = mux.utils.now();
          
          mux.monitor(activeVideoElement, {
            debug: process.env.NODE_ENV === 'development',
            data: {
              env_key: process.env.NEXT_PUBLIC_MUX_ENV_KEY,
              player_name: 'NanoPlayer',
              player_init_time: playerInitTime,
              ...metadata,
            }
          });
        },
        onDestroy: () => {
          if (muxElementRef.current) {
            mux.emit(muxElementRef.current, 'destroy');
            muxElementRef.current = null;
          }
        },
      },
    };

    player.setup(config);

    return () => {
      if (muxElementRef.current) {
        mux.emit(muxElementRef.current, 'destroy');
      }
      if (playerRef.current) {
        playerRef.current.destroy();
      }
    };
  }, [streamConfig, metadata]);

  return <div id="nanoPlayerDiv" style={{ width: '100%', aspectRatio: '16/9' }} />;
}

Important Notes

  • ABR on iOS: NanoPlayer may create multiple <video> elements for smooth ABR switching. Always use the activeVideoElement from the event.
  • Element Changes: The active element can change during playback. Re-initialize Mux monitoring when this happens.
  • Cleanup: Always destroy the previous Mux instance before creating a new one to avoid duplicate tracking.

NanoPlayer Documentation

Updating Metadata Dynamically

Video Change Event

When the video source changes (e.g., playlist advancement), emit a videochange event:

// Using selector
mux.emit('#my-player', 'videochange', {
  video_id: 'new-video-456',
  video_title: 'Next Video in Playlist',
  video_stream_type: 'on-demand',
  video_content_type: 'clip',
});

// Using element reference (React)
mux.emit(videoRef.current, 'videochange', {
  video_id: newClip.id,
  video_title: newClip.title,
  video_stream_type: 'on-demand',
  video_content_type: 'clip',
});

Cleanup / Destroy

Always clean up when unmounting or destroying the player:

// Emit destroy event to end the current view session
mux.emit('#my-player', 'destroy');

// Or with element reference
mux.emit(videoRef.current, 'destroy');

Built-in vs Custom Fields

Built-in Mux Dimensions

These fields are natively supported by Mux and appear in dashboard reports:

FieldPurposeLevel
video_idVideo identifierbasic
video_titleVideo titlebasic
video_stream_typelive/on-demandbasic
video_content_typeContent categorizationbasic
video_creator_idCreator identifieradvanced
viewer_user_idViewer identifieradvanced
sub_property_idSub-property groupingbasic

Custom Fields

Use custom_1 through custom_10 for platform-specific data:

FieldUsage
custom_1Reserved for external user ID (optional, provided by integrators)
custom_2 - custom_10Available for future use

Additional Resources

Backend Analytics

The backend automatically processes analytics data using the video_id field to filter views from Mux's API. Ensure your frontend sends the correct video_id to match backend expectations.

On this page