esite_flutter_player

A secure, production-ready Flutter plugin for DRM-protected video playback on Android using Media3 ExoPlayer with Widevine DRM support.

๐Ÿš€ Quick Start - Testing the Example

The example app includes 7 comprehensive test scenarios! To run it:

cd example
flutter clean && rm -rf build/ .dart_tool/ android/.gradle/
flutter pub get
flutter run

Then tap "โœ… Valid Auth (Should Work)" to test DRM-protected video playback!

๐Ÿ“– See QUICK_START.md for detailed instructions ๐Ÿ“Š See CHANGES_SUMMARY.md for recent updates

Features

  • โœ… DRM Support: Widevine DRM via Axinom integration
  • โœ… Security: Automatic screenshot and screen recording blocking (FLAG_SECURE)
  • โœ… Playback Controls: Play, pause, seek, speed control (0.5x-4x)
  • โœ… Track Selection: Video quality selection
  • โœ… Subtitle Support: Enable/disable subtitles
  • โœ… Volume Control: Volume adjustment and mute
  • โœ… Playlist Support: Next/previous media item navigation
  • โœ… Event Streams: Real-time playback state, DRM events, and error handling
  • โœ… Production Ready: Secure, no DRM data logging, proper lifecycle management

Platform Support

  • โœ… Android: Full support (Media3 ExoPlayer)
  • โŒ iOS: Not yet implemented (planned for future release)

Installation

Add esite_flutter_player to your pubspec.yaml:

dependencies:
  esite_flutter_player: ^0.1.0

Then run:

flutter pub get

Quick Start

1. Basic Setup

import 'package:esite_flutter_player/esite_flutter_player.dart';

// Create player configuration
final config = ESitePlayerConfig(
  sourceUrl: 'https://your-cdn.com/video.mpd',
  drm: ESiteDrmConfig(
    licenseUrl: 'https://license.axinom.com/widevine',
    scheme: ESiteDrmScheme.widevine,
    licenseHeaders: {
      'X-AxDRM-Message': 'your-license-header',
    },
  ),
  autoPlay: false,
);

// Create controller
final controller = ESitePlayerController(config);

// Initialize player
await controller.initialize();

2. Display Video Player

ESitePlayerView(controller: controller)

3. Control Playback

// Play
await controller.play();

// Pause
await controller.pause();

// Seek to position
await controller.seekTo(Duration(seconds: 30));

// Seek forward/backward
await controller.seekForward(Duration(seconds: 10));
await controller.seekBackward(Duration(seconds: 10));

// Set playback speed (0.5x to 4x)
await controller.setPlaybackSpeed(1.5);

// Volume control
await controller.setVolume(0.8);
await controller.setMuted(true);

// Track selection
final tracks = await controller.getAvailableVideoTracks();
await controller.setVideoTrack(tracks[0]['id']);

// Subtitle control
await controller.setSubtitleEnabled(true);

// Playlist navigation
if (await controller.hasNext()) {
  await controller.next();
}

4. Listen to Events

// Playback state
controller.stateStream.listen((state) {
  print('Player state: $state');
  // States: idle, initializing, buffering, ready, playing, paused, completed, error
});

// DRM events
controller.drmStream.listen((event) {
  print('DRM event: ${event.type}');
  // Types: licenseRequested, licenseAcquired, licenseFailed
});

// Errors
controller.errorStream.listen((error) {
  print('Error: ${error.code} - ${error.message}');
});

5. Cleanup

@override
void dispose() {
  controller.dispose();
  super.dispose();
}

Complete Example

See the example app for a full implementation with:

  • Embedded player view
  • Playback controls UI
  • State monitoring
  • Error handling
  • Track selection
  • Volume control

Documentation

Security Features

Screenshot & Screen Recording Protection

The plugin automatically enables FLAG_SECURE when playback starts, preventing:

  • Screenshots
  • Screen recording
  • Recent apps preview capture

Security is automatically:

  • Enabled when playback starts
  • Disabled when playback pauses or ends
  • Disabled when the player is disposed

This ensures security is only active during actual playback, not affecting the rest of your app.

DRM Configuration

Axinom Widevine Setup

ESiteDrmConfig(
  licenseUrl: 'https://license.axinom.com/widevine',
  scheme: ESiteDrmScheme.widevine,
  licenseHeaders: {
    'X-AxDRM-Message': 'your-license-header',
    'Authorization': 'Bearer your-token',
  },
)

Supported DRM Schemes

  • ESiteDrmScheme.widevine - Widevine DRM (Axinom)

API Reference

ESitePlayerController

Main controller for video playback.

Methods

  • Future<void> initialize() - Initialize the player with configuration
  • Future<void> play() - Start playback
  • Future<void> pause() - Pause playback
  • Future<void> seekTo(Duration position) - Seek to specific position
  • Future<void> seekForward(Duration offset) - Seek forward by offset
  • Future<void> seekBackward(Duration offset) - Seek backward by offset
  • Future<void> setPlaybackSpeed(double speed) - Set speed (0.5-4.0)
  • Future<void> setVolume(double volume) - Set volume (0.0-1.0)
  • Future<void> setMuted(bool muted) - Mute/unmute
  • Future<bool> isMuted() - Check if muted
  • Future<double> getVolume() - Get current volume
  • Future<List<Map<String, dynamic>>> getAvailableVideoTracks() - Get video tracks
  • Future<void> setVideoTrack(String trackId) - Select video track
  • Future<void> setSubtitleEnabled(bool enabled) - Enable/disable subtitles
  • Future<bool> isSubtitleEnabled() - Check subtitle status
  • Future<bool> hasNext() - Check if next item available
  • Future<bool> hasPrevious() - Check if previous item available
  • Future<void> next() - Go to next media item
  • Future<void> previous() - Go to previous media item
  • Future<void> dispose() - Dispose player and cleanup

Properties

  • ESitePlayerState currentState - Current playback state
  • Stream<ESitePlayerState> stateStream - Playback state stream
  • Stream<ESiteDrmEvent> drmStream - DRM events stream
  • Stream<ESitePlayerError> errorStream - Error stream

ESitePlayerConfig

Player configuration.

ESitePlayerConfig({
  required String sourceUrl,
  required ESiteDrmConfig drm,
  Map<String, String>? headers,
  bool autoPlay = false,
  bool preventScreenshots = false,
  bool preventScreenRecording = false,
})

ESiteDrmConfig

DRM configuration.

ESiteDrmConfig({
  required String licenseUrl,
  required ESiteDrmScheme scheme,
  Map<String, String>? licenseHeaders,
})

ESitePlayerState

Playback state enum:

  • idle - Initial state
  • initializing - Player initializing
  • buffering - Buffering content
  • ready - Ready to play
  • playing - Currently playing
  • paused - Paused
  • completed - Playback completed
  • error - Error occurred

ESiteDrmEvent

DRM event with type and optional message:

  • licenseRequested - License request initiated
  • licenseAcquired - License successfully acquired
  • licenseFailed - License acquisition failed

ESitePlayerError

Error with code and message:

  • invalidSource - Invalid source URL
  • drmFailure - DRM-related failure
  • networkError - Network error
  • playbackError - Playback error
  • unknown - Unknown error

Limitations

Emulator Support

DRM-protected content may not work on Android emulators due to:

  • Lack of Widevine L1 support
  • Missing hardware security features

Recommendation: Test on physical Android devices.

iOS Support

iOS support is planned for a future release. The plugin currently throws UnsupportedError on iOS.

Testing

This plugin includes comprehensive test coverage across multiple levels:

Running Tests

# Run all unit tests
flutter test

# Run with coverage report
flutter test --coverage

# Run integration tests (requires device/emulator)
cd example
flutter test integration_test/

# Run Android native tests
cd example/android
./gradlew test

# Generate HTML coverage report
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html

Test Coverage

The plugin maintains high test coverage across:

  • โœ… Unit Tests (70%): Controller, configuration, events, platform layer
  • โœ… Widget Tests (20%): Player view and UI components
  • โœ… Integration Tests (10%): End-to-end playback, DRM, security

Coverage Goals:

  • Overall: >85%
  • Controller: >95%
  • Configuration: 100%
  • Events/Errors: 100%

Test Documentation

For detailed testing information, see:

Test Categories

  1. Configuration Tests: Player and DRM configuration validation
  2. Controller Tests: Playback control, state management, streams
  3. Platform Tests: Method channel and event channel communication
  4. Widget Tests: UI component rendering
  5. Integration Tests: Complete workflows, DRM license acquisition, security
  6. Native Tests: Android ExoPlayer and SecurityManager
  7. Error Handling: All error scenarios and recovery
  8. Edge Cases: Boundary conditions and unusual inputs

Troubleshooting

DRM License Fails

  1. Verify license URL is correct
  2. Check license headers (especially Axinom headers)
  3. Ensure device supports Widevine L1 or L3
  4. Test on physical device (not emulator)

Playback Doesn't Start

  1. Check network connectivity
  2. Verify source URL is accessible
  3. Check DRM configuration
  4. Monitor error stream for details

Security Not Working

  • Security (FLAG_SECURE) is only enabled during active playback
  • Ensure player is in playing state
  • Check that activity is properly attached

Contributing

Contributions are welcome! Please ensure:

  • Code follows Dart/Flutter style guidelines
  • Tests are included for new features
  • Documentation is updated

License

See LICENSE file for details.

Support

For issues and questions:

  • Open an issue on GitHub
  • Check the example app for usage patterns

---Note: This plugin is designed for production use with secure DRM content. Always test thoroughly on physical devices before deploying.