> ## Documentation Index
> Fetch the complete documentation index at: https://docs.permutive.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Video Tracking

> Track video playback, engagement, and completion metrics

The `MediaTracker` API allows you to track video content events with Permutive. Use this for tracking video playback, engagement, and completion metrics.

<CardGroup cols={4}>
  <Card title="Basic Usage" href="#basic-usage" icon="play" />

  <Card title="Properties" href="#video-properties" icon="list" />

  <Card title="Use Cases" href="#use-cases" icon="layer-group" />

  <Card title="Ad Tracking" href="#video-ad-tracking" icon="rectangle-ad" />
</CardGroup>

<Info>
  **Connected TV Integration**

  Video event tracking described below is available once the connected TV integration has been enabled. Please contact your Customer Success Manager (CSM) to enable this feature.
</Info>

## Overview

`MediaTracker` provides comprehensive video tracking capabilities:

* **Automatic lifecycle tracking** - Tracks Videoview and VideoComplete events
* **Engagement metrics** - Measures time spent watching and completion percentage
* **Custom events** - Track custom video-related events
* **Rich metadata** - Support for extensive video properties

***

## Video Events

MediaTracker automatically tracks two event types:

### 1. Videoview Event

Tracked when MediaTracker is created:

```
Event: Videoview
Properties:
  - [your custom properties]
  - [standard video properties]
```

### 2. VideoComplete Event

Tracked when video is stopped:

```
Event: VideoComplete
Properties:
  - engaged_time: Total time spent watching (seconds)
  - completion: Percentage of video viewed (0.0 - 1.0)
  - [your custom properties]
  - [standard video properties]
```

> 📘 Note
>
> The `completion` property requires a known duration to be set. Pass `durationMilliseconds` when creating the MediaTracker.

***

## Basic Usage

### Creating a MediaTracker

Create a MediaTracker when video playback begins:

<CodeGroup>
  ```kotlin Kotlin theme={"dark"}
  import com.permutive.android.MediaTracker
  import android.net.Uri

  class VideoActivity : AppCompatActivity() {

      private val permutive by lazy {
          (application as MyApplication).permutive
      }

      private lateinit var videoTracker: MediaTracker

      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)

          videoTracker = permutive.trackVideoView(
              durationMilliseconds = 120000, // 2 minutes
              videoProperties = MediaTracker.VideoProperties(
                  title = "Sample Video",
                  genre = listOf("Documentary"),
                  contentType = listOf("Education"),
                  runtime = 120
              )
          )
      }

      override fun onDestroy() {
          super.onDestroy()
          videoTracker.stop()  // Important: Always stop to send VideoComplete
      }
  }
  ```

  ```java Java theme={"dark"}
  import com.permutive.android.MediaTracker;
  import android.net.Uri;

  public class VideoActivity extends AppCompatActivity {

      private MediaTracker videoTracker;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);

          Permutive permutive = ((MyApplication) getApplication()).getPermutive();

          MediaTracker.VideoProperties videoProps = new MediaTracker.VideoProperties.Builder()
              .title("Sample Video")
              .genre(Arrays.asList("Documentary"))
              .contentType(Arrays.asList("Education"))
              .runtime(120)
              .build();

          videoTracker = permutive.trackVideoView(
              120000, // 2 minutes in milliseconds
              videoProps,
              null,   // pageProperties (optional)
              null    // customEventProperties (optional)
          );
      }

      @Override
      protected void onDestroy() {
          super.onDestroy();
          videoTracker.stop();  // Important: Always stop to send VideoComplete
      }
  }
  ```
</CodeGroup>

***

## MediaTracker Lifecycle

### Expected Usage Flow

1. **Create** MediaTracker instance with properties
2. **Call** `play()` when playback begins
3. **Track buffering** with `pause()` / `play()`
4. **Track scrubbing** with `play(position)`
5. **Call** `stop()` when playback completes

> ⚠️ Single Instance Limitation
>
> Only a single instance of `PageTracker` or `MediaTracker` is available at any time. Close existing trackers before creating new ones.

### Playback Control Methods

#### play()

Call when video starts playing:

```kotlin theme={"dark"}
videoTracker.play()
```

#### play(positionMs)

Call when playback position changes (e.g., seeking):

```kotlin theme={"dark"}
videoTracker.play(positionMs = 30000)  // Seek to 30 seconds
```

#### pause()

Call when video is paused or buffering:

```kotlin theme={"dark"}
videoTracker.pause()
```

#### stop()

Call when video playback completes or user exits:

```kotlin theme={"dark"}
videoTracker.stop()  // Sends VideoComplete event
```

> ⚠️ Important: Always Call stop()
>
> Failing to call `stop()` will prevent VideoComplete events from being tracked.

***

## Video Properties

### Standard Video Properties

The SDK provides standard video properties for consistent tracking:

```kotlin theme={"dark"}
MediaTracker.VideoProperties(
    title = "Video Title",
    genre = listOf("Action", "Thriller"),
    contentType = listOf("Movie", "Feature"),
    ageRating = "PG-13",
    runtime = 7200,  // Runtime in seconds
    country = "US",
    originalLanguage = "en",
    audioLanguage = "en",
    areSubtitlesEnabled = true,
    subtitlesLanguage = "en",
    seasonNumber = 1,
    episodeNumber = 5,
    consecutiveEpisodes = 3,
    iabCategories = listOf("IAB1", "IAB2")
)
```

### Property Reference

| Property                | Type          | Description                             |
| ----------------------- | ------------- | --------------------------------------- |
| **title**               | String        | Video title                             |
| **genre**               | List\<String> | Video genres (e.g., "Drama", "Comedy")  |
| **contentType**         | List\<String> | Content types (e.g., "Movie", "Series") |
| **ageRating**           | String        | Age rating (e.g., "PG-13", "TV-MA")     |
| **runtime**             | Int           | Runtime in **seconds**                  |
| **country**             | String        | Origin country                          |
| **originalLanguage**    | String        | Original language code                  |
| **audioLanguage**       | String        | Audio language code                     |
| **areSubtitlesEnabled** | Boolean       | Whether subtitles are enabled           |
| **subtitlesLanguage**   | String        | Subtitle language code                  |
| **seasonNumber**        | Int           | Season number (for series)              |
| **episodeNumber**       | Int           | Episode number (for series)             |
| **consecutiveEpisodes** | Int           | Consecutive episodes watched            |
| **iabCategories**       | List\<String> | IAB content taxonomy categories         |

> 💡 Best Practice
>
> Track as many properties as possible to enable richer cohort creation and insights.

***

## Page Properties (Optional)

If the video is displayed within a page context (e.g., embedded in article), include page properties:

```kotlin theme={"dark"}
videoTracker = permutive.trackVideoView(
    durationMilliseconds = 120000,
    videoProperties = MediaTracker.VideoProperties(
        title = "Tutorial Video"
    ),
    pageProperties = MediaTracker.PageProperties(
        title = "How to Use Android SDK",
        url = Uri.parse("https://example.com/tutorial"),
        referrer = Uri.parse("https://example.com/docs")
    )
)
```

### PageProperties Reference

| Property     | Type   | Description        |
| ------------ | ------ | ------------------ |
| **title**    | String | Page title         |
| **url**      | Uri    | Page URL           |
| **referrer** | Uri    | Referring page URL |

***

## Custom Properties

Add custom properties specific to your event schema:

```kotlin theme={"dark"}
import com.permutive.android.EventProperties

videoTracker = permutive.trackVideoView(
    durationMilliseconds = 120000,
    videoProperties = videoProps,
    pageProperties = null,
    customEventProperties = EventProperties.from(
        "video_id" to "vid_12345",
        "playlist_id" to "playlist_abc",
        "is_premium_content" to true,
        "content_partner" to "PartnerXYZ"
    )
)
```

***

## Complete Example

<CodeGroup>
  ```kotlin Kotlin theme={"dark"}
  import com.permutive.android.MediaTracker
  import android.net.Uri
  import androidx.appcompat.app.AppCompatActivity

  class VideoPlayerFragment : Fragment() {

      private val permutive by lazy {
          (requireActivity().application as MyApplication).permutive
      }

      private lateinit var videoTracker: MediaTracker
      private lateinit var exoPlayer: ExoPlayer

      override fun onCreateView(
          inflater: LayoutInflater,
          container: ViewGroup?,
          savedInstanceState: Bundle?
      ): View? {
          // Initialize video tracker
          videoTracker = permutive.trackVideoView(
              durationMilliseconds = 180000, // 3 minutes
              videoProperties = MediaTracker.VideoProperties(
                  title = "Introduction to Android",
                  genre = listOf("Educational", "Technology"),
                  contentType = listOf("Tutorial"),
                  ageRating = "G",
                  runtime = 180,
                  country = "US",
                  originalLanguage = "en",
                  audioLanguage = "en",
                  areSubtitlesEnabled = false,
                  iabCategories = listOf("IAB19")
              ),
              pageProperties = MediaTracker.PageProperties(
                  title = "Android Tutorial Page",
                  url = Uri.parse("https://example.com/tutorials/android"),
                  referrer = Uri.parse("https://example.com/home")
              ),
              customEventProperties = EventProperties.from(
                  "video_id" to "vid_tutorial_001",
                  "is_premium" to false
              )
          )

          // Set up ExoPlayer with tracking
          setupVideoPlayer()

          return inflater.inflate(R.layout.fragment_video, container, false)
      }

      private fun setupVideoPlayer() {
          exoPlayer = ExoPlayer.Builder(requireContext()).build()

          exoPlayer.addListener(object : Player.Listener {
              override fun onPlaybackStateChanged(state: Int) {
                  when (state) {
                      Player.STATE_READY -> {
                          // Video ready to play
                          if (exoPlayer.isPlaying) {
                              videoTracker.play()
                          }
                      }
                      Player.STATE_BUFFERING -> {
                          videoTracker.pause()
                      }
                  }
              }

              override fun onIsPlayingChanged(isPlaying: Boolean) {
                  if (isPlaying) {
                      videoTracker.play()
                  } else {
                      videoTracker.pause()
                  }
              }
          })

          // Track position updates
          exoPlayer.addAnalyticsListener(object : AnalyticsListener {
              override fun onPositionDiscontinuity(
                  eventTime: AnalyticsListener.EventTime,
                  reason: Int
              ) {
                  if (reason == Player.DISCONTINUITY_REASON_SEEK) {
                      videoTracker.play(exoPlayer.currentPosition)
                  }
              }
          })
      }

      override fun onPause() {
          super.onPause()
          videoTracker.pause()
          exoPlayer.pause()
      }

      override fun onResume() {
          super.onResume()
          if (exoPlayer.isPlaying) {
              videoTracker.play()
          }
      }

      override fun onDestroy() {
          super.onDestroy()
          videoTracker.stop()  // Send VideoComplete event
          exoPlayer.release()
      }
  }
  ```

  ```java Java theme={"dark"}
  import com.permutive.android.MediaTracker;
  import android.net.Uri;

  public class VideoPlayerFragment extends Fragment {

      private MediaTracker videoTracker;
      private ExoPlayer exoPlayer;

      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          Permutive permutive = ((MyApplication) requireActivity().getApplication()).getPermutive();

          // Build video properties
          MediaTracker.VideoProperties videoProps = new MediaTracker.VideoProperties.Builder()
              .title("Introduction to Android")
              .genre(Arrays.asList("Educational", "Technology"))
              .contentType(Arrays.asList("Tutorial"))
              .ageRating("G")
              .runtime(180)
              .country("US")
              .originalLanguage("en")
              .audioLanguage("en")
              .areSubtitlesEnabled(false)
              .iabCategories(Arrays.asList("IAB19"))
              .build();

          // Build page properties
          MediaTracker.PageProperties pageProps = new MediaTracker.PageProperties(
              "Android Tutorial Page",
              Uri.parse("https://example.com/tutorials/android"),
              Uri.parse("https://example.com/home")
          );

          // Build custom properties
          EventProperties customProps = new EventProperties.Builder()
              .with("video_id", "vid_tutorial_001")
              .with("is_premium", false)
              .build();

          // Create video tracker
          videoTracker = permutive.trackVideoView(
              180000, // 3 minutes
              videoProps,
              pageProps,
              customProps
          );

          setupVideoPlayer();

          return inflater.inflate(R.layout.fragment_video, container, null);
      }

      private void setupVideoPlayer() {
          exoPlayer = new ExoPlayer.Builder(requireContext()).build();

          exoPlayer.addListener(new Player.Listener() {
              @Override
              public void onPlaybackStateChanged(int state) {
                  if (state == Player.STATE_READY && exoPlayer.isPlaying()) {
                      videoTracker.play();
                  } else if (state == Player.STATE_BUFFERING) {
                      videoTracker.pause();
                  }
              }

              @Override
              public void onIsPlayingChanged(boolean isPlaying) {
                  if (isPlaying) {
                      videoTracker.play();
                  } else {
                      videoTracker.pause();
                  }
              }
          });
      }

      @Override
      public void onPause() {
          super.onPause();
          videoTracker.pause();
          exoPlayer.pause();
      }

      @Override
      public void onResume() {
          super.onResume();
          if (exoPlayer.isPlaying()) {
              videoTracker.play();
          }
      }

      @Override
      public void onDestroy() {
          super.onDestroy();
          videoTracker.stop();
          exoPlayer.release();
      }
  }
  ```
</CodeGroup>

***

## Tracking Custom Events

Track custom video-related events using the `track()` method:

```kotlin theme={"dark"}
// User shares video
videoTracker.track(
    "VideoShared",
    EventProperties.from(
        "share_method" to "twitter",
        "video_position_seconds" to 45
    )
)

// User adds to favorites
videoTracker.track("VideoAddedToFavorites")

// Quality changed
videoTracker.track(
    "VideoQualityChanged",
    EventProperties.from(
        "from_quality" to "720p",
        "to_quality" to "1080p"
    )
)
```

***

## Video Ad Tracking

If you need to track video ads within video content, use the `AdTracker` API:

```kotlin theme={"dark"}
// Create ad tracker from video tracker
val adTracker = videoTracker.trackAdView(
    durationMs = 15000,  // 15 second ad
    adProperties = AdTracker.AdProperties(
        title = "Product Ad",
        durationInSeconds = 15,
        isMuted = false,
        campaignId = "campaign_123",
        creativeId = "creative_456"
    )
)

// Track ad playback
adTracker.play()
// ... ad plays ...
adTracker.completion()
```

See [Video Ad Tracking](/sdks/mobile/android/features/video-ad-tracking) for complete documentation.

***

## Use Cases

<AccordionGroup>
  <Accordion title="Movie/TV App" icon="film">
    ```kotlin theme={"dark"}
    videoTracker = permutive.trackVideoView(
        durationMilliseconds = movieDurationMs,
        videoProperties = MediaTracker.VideoProperties(
            title = movie.title,
            genre = movie.genres,
            contentType = listOf("Movie", "Feature"),
            ageRating = movie.rating,
            runtime = movie.runtimeSeconds,
            country = movie.country,
            originalLanguage = movie.language,
            iabCategories = movie.iabCategories
        )
    )
    ```
  </Accordion>

  <Accordion title="Educational/Tutorial App" icon="graduation-cap">
    ```kotlin theme={"dark"}
    videoTracker = permutive.trackVideoView(
        durationMilliseconds = tutorialDurationMs,
        videoProperties = MediaTracker.VideoProperties(
            title = tutorial.title,
            genre = listOf("Educational"),
            contentType = listOf("Tutorial"),
            runtime = tutorial.runtimeSeconds
        ),
        customEventProperties = EventProperties.from(
            "course_id" to tutorial.courseId,
            "module_id" to tutorial.moduleId,
            "difficulty_level" to tutorial.difficulty
        )
    )
    ```
  </Accordion>

  <Accordion title="Series/Episode Tracking" icon="tv">
    ```kotlin theme={"dark"}
    videoTracker = permutive.trackVideoView(
        durationMilliseconds = episodeDurationMs,
        videoProperties = MediaTracker.VideoProperties(
            title = episode.title,
            genre = series.genres,
            contentType = listOf("Series", "Episode"),
            seasonNumber = episode.season,
            episodeNumber = episode.number,
            consecutiveEpisodes = userWatchData.consecutiveCount,
            runtime = episode.runtimeSeconds
        )
    )
    ```
  </Accordion>
</AccordionGroup>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="VideoComplete Not Tracked">
    **Problem:** VideoComplete events not appearing.

    **Cause:** `stop()` not called.

    **Solution:** Always call `stop()` in `onDestroy()`:

    ```kotlin theme={"dark"}
    override fun onDestroy() {
        super.onDestroy()
        videoTracker.stop()  // REQUIRED
    }
    ```
  </Accordion>

  <Accordion title="Engagement Time Incorrect">
    **Problem:** Engaged time is inaccurate.

    **Causes:**

    1. Not calling `play()` when video plays
    2. Not calling `pause()` when video pauses/buffers

    **Solution:** Track all play/pause events:

    ```kotlin theme={"dark"}
    exoPlayer.addListener(object : Player.Listener {
        override fun onIsPlayingChanged(isPlaying: Boolean) {
            if (isPlaying) {
                videoTracker.play()
            } else {
                videoTracker.pause()
            }
        }
    })
    ```
  </Accordion>

  <Accordion title="Completion Percentage Missing">
    **Problem:** `completion` property is missing or null.

    **Cause:** Duration not provided when creating MediaTracker.

    **Solution:** Always provide `durationMilliseconds`:

    ```kotlin theme={"dark"}
    videoTracker = permutive.trackVideoView(
        durationMilliseconds = videoDurationMs  // REQUIRED for completion
    )
    ```
  </Accordion>

  <Accordion title="Multiple Tracker Error">
    **Problem:** Error about multiple trackers.

    **Cause:** Creating new PageTracker or MediaTracker without closing existing one.

    **Solution:** Close existing tracker first:

    ```kotlin theme={"dark"}
    // Close old tracker
    videoTracker.stop()

    // Now create new tracker
    videoTracker = permutive.trackVideoView(...)
    ```

    See [Common Errors](/sdks/mobile/android/troubleshooting/common-errors) for more troubleshooting.
  </Accordion>
</AccordionGroup>

***

## Best Practices

<Tabs>
  <Tab title="Do">
    * Always call `stop()` when video finishes
    * Provide duration for completion tracking
    * Track play/pause events accurately
    * Include as many video properties as possible
    * Close tracker before creating a new one
    * Track buffering with pause/play
  </Tab>

  <Tab title="Don't">
    * Forget to call `stop()`
    * Create multiple MediaTrackers simultaneously
    * Skip play/pause lifecycle events
    * Omit video duration
  </Tab>
</Tabs>

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Video Ad Tracking" icon="rectangle-ad" href="/sdks/mobile/android/features/video-ad-tracking">
    Track video advertisements
  </Card>

  <Card title="Page Tracking" icon="file" href="/sdks/mobile/android/features/page-tracking">
    Track non-video content
  </Card>

  <Card title="Event Properties" icon="list" href="/sdks/mobile/android/core-concepts/event-properties">
    Custom properties
  </Card>

  <Card title="Issues" icon="wrench" href="/sdks/mobile/android/troubleshooting/common-errors">
    Solve common issues
  </Card>
</CardGroup>

***

## API Reference

For complete API documentation, see the [Javadocs](https://sdk-docs.permutive.com/index.html).

### MediaTracker Interface

* `play()` - Start/resume video playback
* `play(positionMs: Long)` - Start at specific position
* `pause()` - Pause video playback
* `stop()` - Complete video tracking (sends VideoComplete)
* `setDuration(durationMs: Long)` - Update video duration
* `track(eventName: String)` - Track custom event
* `track(eventName: String, properties: EventProperties)` - Track custom event with properties
* `trackAdView(...)` - Create AdTracker for video ad

### Creating MediaTracker

* `Permutive.trackVideoView(durationMilliseconds, videoProperties, pageProperties, customEventProperties)` - Create MediaTracker
