esite_flutter_player 0.1.1 copy "esite_flutter_player: ^0.1.1" to clipboard
esite_flutter_player: ^0.1.1 copied to clipboard

PlatformAndroid

Professional Flutter plugin for secure DRM-protected video playback on Android. Features Widevine/PlayReady DRM, emulator detection, screenshot/screen recording blocking (FLAG_SECURE), adaptive strea [...]

ESite Flutter Player #

pub package License: MIT Platform

A professional, production-ready Flutter plugin for secure DRM-protected video playback on Android.

Built on Media3 ExoPlayer, this plugin provides enterprise-grade video streaming with comprehensive security features, making it ideal for premium content delivery, OTT platforms, and educational applications.

โœจ Key Features #

๐Ÿ”’ Enterprise Security #

  • DRM Protection: Industry-standard Widevine and PlayReady DRM support
  • Screen Protection: Automatic screenshot and screen recording blocking (FLAG_SECURE)
  • Emulator Detection: Block playback on emulators to prevent piracy
  • Security Callbacks: Real-time security violation notifications
  • Configurable Levels: Maximum, custom, or disabled security modes

๐ŸŽฌ Professional Playback #

  • Adaptive Streaming: Automatic quality adaptation for optimal viewing
  • Format Support: DASH (.mpd) and HLS (.m3u8) streaming protocols
  • Speed Control: Variable playback speed (0.5x to 4.0x)
  • Quality Selection: Manual video track/quality switching
  • Subtitle Support: Enable/disable subtitle tracks
  • Playlist Management: Navigate between media items

๐ŸŽฏ Production Ready #

  • Memory Optimized: Efficient resource management
  • Thread Safe: Proper lifecycle and synchronization
  • Error Handling: User-friendly error messages with recovery guidance
  • Event Streams: Real-time state, DRM, and error notifications
  • Null Safety: Full Dart null-safety support

๐Ÿ“ฑ Platform Support #

Platform Supported Version
Android โœ… API 24+ (Android 7.0+)
iOS โณ Planned for future release

๐ŸŽฅ Supported Formats #

  • DASH (.mpd) - Dynamic Adaptive Streaming over HTTP
  • HLS (.m3u8) - HTTP Live Streaming
  • DRM Schemes: Widevine, PlayReady

๐Ÿ“ฆ Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  esite_flutter_player: ^0.1.0

Then run:

flutter pub get

๐Ÿš€ Quick Start #

1. Import the Package #

import 'package:esite_flutter_player/esite_flutter_player.dart';

2. Create Player Configuration #

final config = ESitePlayerConfig(
  sourceUrl: 'https://example.com/video.mpd',
  drm: ESiteDrmConfig(
    licenseUrl: 'https://drm-license-server.com/license',
    scheme: ESiteDrmScheme.widevine,
    licenseHeaders: {
      'Authorization': 'Bearer your_token_here',
    },
  ),
  autoPlay: false,
  // Security configuration (recommended)
  securityConfig: ESiteSecurityConfig.maximum(
    onSecurityViolation: (violation) {
      print('Security alert: ${violation.message}');
    },
  ),
);

3. Initialize Controller #

final controller = ESitePlayerController(config);
await controller.initialize();

4. Add Player to Your Widget Tree #

@override
Widget build(BuildContext context) {
  return ESitePlayerView(controller: controller);
}

5. Control Playback #

// Play/Pause
await controller.play();
await controller.pause();

// Seek
await controller.seekTo(Duration(seconds: 30));
await controller.seekForward(Duration(seconds: 10));
await controller.seekBackward(Duration(seconds: 10));

// Speed control
await controller.setPlaybackSpeed(1.5); // 0.5x to 4.0x

// Volume
await controller.setVolume(0.8); // 0.0 to 1.0
await controller.setMuted(true);

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

// Subtitles
await controller.setSubtitleEnabled(true);

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

6. Listen to Events #

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

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

// Error handling
controller.errorStream.listen((error) {
  print('Error: ${error.code} - ${error.message}');
  // Detailed, user-friendly error messages
});

7. Cleanup #

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

๐Ÿ“– Complete Example #

import 'package:flutter/material.dart';
import 'package:esite_flutter_player/esite_flutter_player.dart';

class VideoPlayerPage extends StatefulWidget {
  @override
  _VideoPlayerPageState createState() => _VideoPlayerPageState();
}

class _VideoPlayerPageState extends State<VideoPlayerPage> {
  late ESitePlayerController controller;
  ESitePlayerState currentState = ESitePlayerState.idle;

  @override
  void initState() {
    super.initState();
    
    final config = ESitePlayerConfig(
      sourceUrl: 'https://media.axprod.net/TestVectors/Cmaf/protected_1080p_h264_cbcs/manifest.mpd',
      drm: ESiteDrmConfig(
        licenseUrl: 'https://drm-widevine-licensing.axprod.net/AcquireLicense?AxDrmMessage=...',
        scheme: ESiteDrmScheme.widevine,
        licenseHeaders: {},
      ),
      autoPlay: false,
      securityConfig: ESiteSecurityConfig.maximum(),
    );
    
    controller = ESitePlayerController(config);
    controller.initialize();
    
    controller.stateStream.listen((state) {
      setState(() => currentState = state);
    });
    
    controller.errorStream.listen((error) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error: ${error.message}')),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Video Player')),
      body: Column(
        children: [
          Expanded(
            child: ESitePlayerView(controller: controller),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              IconButton(
                icon: Icon(Icons.replay_10),
                onPressed: () => controller.seekBackward(Duration(seconds: 10)),
              ),
              IconButton(
                icon: Icon(
                  currentState == ESitePlayerState.playing 
                    ? Icons.pause 
                    : Icons.play_arrow,
                ),
                iconSize: 48,
                onPressed: () {
                  if (currentState == ESitePlayerState.playing) {
                    controller.pause();
                  } else {
                    controller.play();
                  }
                },
              ),
              IconButton(
                icon: Icon(Icons.forward_10),
                onPressed: () => controller.seekForward(Duration(seconds: 10)),
              ),
            ],
          ),
        ],
      ),
    );
  }

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

See the example app for a comprehensive implementation with:

  • 10 test scenarios including:
    • Valid authentication with DRM
    • Error cases (expired auth, invalid URLs, network errors)
    • Security scenarios (screen protection, emulator blocking)
  • Full UI controls (play, pause, seek, speed, volume)
  • Real-time event logging
  • Error handling with retry functionality
  • Quality selection interface
  • Security violation handling

๐Ÿ”’ Security Features #

Screen Protection #

The plugin provides comprehensive screen protection features:

  • โœ… Screenshots blocked - Prevents screenshot capture
  • โœ… Screen recording blocked - Prevents screen recording
  • โœ… Recent apps protection - Blurs content in recent apps switcher

Using Security Configuration (Recommended):

final config = ESitePlayerConfig(
  sourceUrl: 'https://example.com/video.mpd',
  drm: ESiteDrmConfig(
    licenseUrl: 'https://license-server.com/license',
    scheme: ESiteDrmScheme.widevine,
  ),
  // Maximum security (default)
  securityConfig: ESiteSecurityConfig.maximum(
    onSecurityViolation: (violation) {
      print('Security violation: ${violation.message}');
    },
  ),
);

Security Configurations:

// Maximum security (recommended for production)
securityConfig: ESiteSecurityConfig.maximum()

// Custom configuration
securityConfig: ESiteSecurityConfig(
  blockEmulators: true,              // Block playback on emulators
  enableScreenProtection: true,       // Enable screen capture blocking
  onSecurityViolation: (violation) {  // Optional callback
    // Handle security violations
    switch (violation.type) {
      case SecurityViolationType.emulatorDetected:
        showError('Playback not supported on emulators');
        break;
      case SecurityViolationType.screenCaptureAttempt:
        showError('Screen recording is blocked');
        break;
    }
  },
)

// Development mode (all security disabled)
securityConfig: ESiteSecurityConfig.disabled()

Backward Compatibility:

The deprecated flags still work but use the new securityConfig instead:

// โŒ Deprecated (still works)
preventScreenshots: true,
preventScreenRecording: true,

// โœ… Recommended
securityConfig: ESiteSecurityConfig(
  enableScreenProtection: true,
)

Emulator Detection #

Protect premium content by blocking playback on emulators:

securityConfig: ESiteSecurityConfig(
  blockEmulators: true,  // Will prevent playback on emulators
  onSecurityViolation: (violation) {
    if (violation.type == SecurityViolationType.emulatorDetected) {
      // Show user-friendly error message
      showDialog(
        context: context,
        builder: (context) => AlertDialog(
          title: Text('Device Not Supported'),
          content: Text('Playback is not available on emulators'),
        ),
      );
    }
  },
)

Detection Methods:

  • Build properties analysis (product, model, brand, device)
  • Hardware identifiers (goldfish, ranchu)
  • Emulator-specific files (/dev/qemu_pipe, etc.)
  • System properties (ro.kernel.qemu, ro.hardware)
  • Known emulators (Genymotion, BlueStacks, NOX, Andy)

Security Behavior:

  • ๐ŸŸข Enabled when playback starts
  • ๐Ÿ”ด Disabled when playback pauses or ends
  • ๐Ÿ”ด Disabled when player is disposed
  • ๐Ÿ” Emulator check runs during initialization

This ensures security is active only during actual content playback.

DRM Protection #

  • Widevine DRM support for encrypted content
  • PlayReady support
  • Secure license acquisition with custom headers
  • No DRM tokens or license details logged
  • Generic error messages to prevent information leakage

โš™๏ธ Configuration Options #

ESitePlayerConfig #

Parameter Type Required Default Description
sourceUrl String Yes - Video source URL (.mpd or .m3u8)
drm ESiteDrmConfig Yes - DRM configuration
securityConfig ESiteSecurityConfig No ESiteSecurityConfig() Security settings
headers Map<String, String>? No null Custom HTTP headers for media requests
autoPlay bool No false Start playback automatically
preventScreenshots bool No false [Deprecated] Use securityConfig instead
preventScreenRecording bool No false [Deprecated] Use securityConfig instead

ESiteSecurityConfig #

Parameter Type Default Description
blockEmulators bool true Block playback on emulators
enableScreenProtection bool true Enable screen capture/recording protection
onSecurityViolation Function? null Callback for security violations

ESiteDrmConfig #

Parameter Type Required Description
licenseUrl String Yes DRM license server URL
scheme ESiteDrmScheme Yes DRM scheme (widevine/playready)
licenseHeaders Map<String, String>? No Custom headers for license requests

๐Ÿ“Š API Reference #

Controller Methods #

Method Return Type Description
initialize() Future<void> Initialize player with configuration
play() Future<void> Start or resume playback
pause() Future<void> Pause playback
seekTo(Duration) Future<void> Seek to specific position
seekForward(Duration) Future<void> Seek forward by offset
seekBackward(Duration) Future<void> Seek backward by offset
setPlaybackSpeed(double) Future<void> Set speed (0.5-4.0)
setVolume(double) Future<void> Set volume (0.0-1.0)
setMuted(bool) Future<void> Mute/unmute audio
isMuted() Future<bool> Check if muted
getVolume() Future<double> Get current volume
getAvailableVideoTracks() Future<List<Map>> Get available quality tracks
setVideoTrack(String) Future<void> Select quality track
setSubtitleEnabled(bool) Future<void> Enable/disable subtitles
isSubtitleEnabled() Future<bool> Check subtitle status
hasNext() Future<bool> Check if next item exists
hasPrevious() Future<bool> Check if previous item exists
next() Future<void> Navigate to next item
previous() Future<void> Navigate to previous item
dispose() Future<void> Cleanup and release resources

Event Streams #

Stream Type Description
stateStream Stream<ESitePlayerState> Player state changes
drmStream Stream<ESiteDrmEvent> DRM-related events
errorStream Stream<ESitePlayerError> Error events

Player States #

  • idle - Initial state before initialization
  • initializing - Player is initializing
  • buffering - Loading content
  • ready - Ready to play
  • playing - Currently playing
  • paused - Playback paused
  • completed - Playback finished
  • error - Error occurred

DRM Event Types #

  • licenseRequested - License acquisition started
  • licenseAcquired - License successfully obtained
  • licenseFailed - License acquisition failed

Error Codes #

  • invalidSource - Invalid or inaccessible source URL
  • drmFailure - DRM license or provisioning failure
  • networkError - Network connection issues
  • playbackError - General playback errors
  • unknown - Unclassified errors

๐Ÿงช Testing #

The plugin includes a comprehensive example app with 5 essential test scenarios:

cd example
flutter run

Test Scenarios:

  1. โœ… Valid Auth - No Security - Basic DRM playback without security (development mode)
  2. โœ… Valid Auth - Full Security - Production mode with complete security features
  3. โŒ Invalid Authentication - Network and authentication error handling
  4. ๐Ÿšซ Emulator Detection - Tests emulator blocking (blocks on emulator, works on device)
  5. ๐Ÿ”’ Screenshot Protection - Tests screen capture prevention (try screenshot during playback)

Each scenario demonstrates proper error handling, security features, and user feedback.

Testing on Different Devices #

On Android Emulator:

  • Scenarios 1, 2, 3, 5: Should work (with security active)
  • Scenario 4: Should block with error message

On Physical Device:

  • All scenarios should work
  • Scenario 4 proves emulator detection doesn't block real devices
  • Scenario 5 proves screenshot blocking works (try capturing screen during playback)

โš ๏ธ Known Limitations #

Android Emulator #

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

  • Limited Widevine L3 support (L1 typically unavailable)
  • Missing hardware security features
  • Incomplete DRM stack implementation

Recommendation: Always test on physical Android devices for DRM functionality.

iOS Platform #

iOS support is planned but not yet implemented. The plugin currently throws UnsupportedError on iOS platforms.

๐Ÿ”ง Troubleshooting #

"No suitable media source factory found" #

Cause: Missing DASH/HLS dependencies Solution: Plugin automatically includes required dependencies. If error persists:

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

DRM License Acquisition Fails #

Possible Causes:

  • Invalid or expired license token
  • Incorrect license URL
  • Missing required headers
  • Device DRM level incompatibility

Solutions:

  1. Verify license URL and headers
  2. Check token expiration
  3. Test on L1/L3 compatible device
  4. Review error messages in errorStream

Video Doesn't Play #

Checklist:

  • โœ… Internet connection available
  • โœ… Source URL accessible
  • โœ… DRM configuration correct
  • โœ… Testing on physical device (not emulator)
  • โœ… Listening to errorStream for details

Performance Issues #

Optimization Tips:

  • Use appropriate quality tracks for device
  • Ensure good network connectivity
  • Avoid multiple player instances
  • Properly dispose controllers when done

๐Ÿค Contributing #

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Update documentation
  5. Submit a pull request

Please follow:

  • Dart/Flutter style guidelines
  • Include tests for new features
  • Update README for API changes

๐Ÿ“„ License #

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ™ Acknowledgments #

๐Ÿ“ž Support #

For issues, questions, or contributions:


Made with โค๏ธ for secure video streaming in Flutter

  • โœ… 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 5 test scenarios:

  1. โœ… Valid Auth - No Security
  2. โœ… Valid Auth - Full Security
  3. โŒ Invalid Authentication
  4. ๐Ÿšซ Emulator Detection
  5. ๐Ÿ”’ Screenshot Protection

๐Ÿ”’ Security Configuration #

ESiteSecurityConfig #

Configure security features to protect your premium content.

Security Options

Parameter Type Default Description
blockEmulators bool true Block playback on emulators
enableScreenProtection bool true Enable screenshot/recording blocking
onSecurityViolation Function? null Callback for security events

Security Presets

1. Maximum Security (Production)

ESitePlayerConfig(
  sourceUrl: videoUrl,
  drm: drmConfig,
  securityConfig: ESiteSecurityConfig.maximum(
    onSecurityViolation: (violation) {
      print('Security alert: ${violation.message}');
      analytics.logSecurityEvent(violation.type.name);
    },
  ),
)

โœ… Emulator detection enabled
โœ… Screenshot blocking enabled
โœ… Security monitoring active

2. Custom Configuration

securityConfig: ESiteSecurityConfig(
  blockEmulators: true,              // Block emulators
  enableScreenProtection: true,      // Block screenshots
  onSecurityViolation: (violation) {
    // Handle security events
  },
)

3. Development Mode

securityConfig: ESiteSecurityConfig.disabled()  // All security off

4. Selective Security

// Emulator detection only
ESiteSecurityConfig(blockEmulators: true, enableScreenProtection: false)

// Screenshot protection only
ESiteSecurityConfig(blockEmulators: false, enableScreenProtection: true)

Screenshot & Screen Recording Protection #

Automatically blocks screenshots and screen recording using Android's FLAG_SECURE.

What's Protected:

  • โœ… Screenshots (Power + Volume Down)
  • โœ… Screen recording apps
  • โœ… Recent apps preview
  • โœ… Display mirroring (some cases)

Security Lifecycle:

  • ๐ŸŸข Enabled when playback starts
  • ๐Ÿ”ด Disabled when paused or ended
  • ๐Ÿ”ด Disabled when player disposed

Example:

ESitePlayerConfig(
  sourceUrl: videoUrl,
  drm: drmConfig,
  securityConfig: ESiteSecurityConfig(
    enableScreenProtection: true,
  ),
)

Emulator Detection #

Multi-method emulator detection to prevent automated piracy.

Detection Methods:

  • Build properties analysis
  • Hardware identifier checks
  • Emulator-specific file detection
  • System property inspection
  • Known emulator signatures

Behavior:

  • On Emulator: Playback blocked with error
  • On Real Device: Plays normally

Configuration:

ESitePlayerConfig(
  sourceUrl: videoUrl,
  drm: drmConfig,
  securityConfig: ESiteSecurityConfig(
    blockEmulators: true,
    onSecurityViolation: (violation) {
      if (violation.type == SecurityViolationType.emulatorDetected) {
        showDialog('Playback not available on emulators');
      }
    },
  ),
)

Testing:

  1. Test on Android Studio emulator โ†’ Should block
  2. Test on physical device โ†’ Should play

Security Violation Callbacks #

Monitor security events in real-time:

securityConfig: ESiteSecurityConfig.maximum(
  onSecurityViolation: (SecurityViolation violation) {
    // Log security incident
    logger.warn('Security: ${violation.type} - ${violation.message}');
    
    // Track in analytics
    analytics.logEvent('security_violation', {
      'type': violation.type.name,
      'timestamp': violation.timestamp,
    });
    
    // Handle based on type
    switch (violation.type) {
      case SecurityViolationType.emulatorDetected:
        showError('Device not supported');
        break;
      case SecurityViolationType.screenCaptureAttempt:
        showWarning('Screen recording blocked');
        break;
    }
  },
)

Violation Types:

  • emulatorDetected - Playback blocked on emulator
  • screenCaptureAttempt - Screenshot/recording attempt
  • rootDetected - Rooted device (future)
  • tampering - App tampering (future)

Environment-Based Configuration #

ESiteSecurityConfig getSecurityConfig() {
  if (kDebugMode) {
    return ESiteSecurityConfig.disabled();  // Development
  }
  if (environment == 'staging') {
    return ESiteSecurityConfig(
      blockEmulators: false,
      enableScreenProtection: true,
    );
  }
  return ESiteSecurityConfig.maximum();  // Production
}

Backward Compatibility #

Old flags still work but use securityConfig instead:

// โŒ Deprecated (still works)
ESitePlayerConfig(
  preventScreenshots: true,
  preventScreenRecording: true,
)

// โœ… Recommended
ESitePlayerConfig(
  securityConfig: ESiteSecurityConfig(
    enableScreenProtection: true,
  ),
)

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%

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.
0
likes
130
points
150
downloads

Publisher

verified publisheresite-lab.com

Weekly Downloads

Professional Flutter plugin for secure DRM-protected video playback on Android. Features Widevine/PlayReady DRM, emulator detection, screenshot/screen recording blocking (FLAG_SECURE), adaptive streaming, quality selection, and comprehensive playback controls using Media3 ExoPlayer. Enterprise-ready security for premium content delivery.

Topics

#flutter #video #drm #exoplayer #security

Documentation

API reference

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on esite_flutter_player

Packages that implement esite_flutter_player