bmdrm_mobile 0.0.15
bmdrm_mobile: ^0.0.15 copied to clipboard
A Flutter plugin for DRM-protected video playback with watermark support.
BMDRM Mobile #
A Flutter plugin for DRM-protected video playback with watermark support.
Features #
- DRM Support: Supports Widevine, FairPlay, and PlayReady DRM systems
- Resume Playback: Remembers the last watched position per video on the device and resumes automatically
- Watermark Overlay: Dynamic and static watermark support with customizable positioning and styling
- Platform Optimized: Native Android implementation using ExoPlayer with Media3
- Cryptographic Security: Real ECDSA signature generation and ECDH key exchange
- Session Management: Automatic DRM session generation and management
- Error Handling: Comprehensive error handling and logging
- Code Obfuscation: Production-ready obfuscation support for both Android and iOS
Installation #
Add this to your pubspec.yaml:
dependencies:
bmdrm_mobile: ^0.0.6
Usage #
Basic Usage #
import 'package:bmdrm_mobile/bmdrm_mobile.dart';
// Automatic DRM session generation
BmdrmVideoPlayer.drmMinimal(
apiKey: 'your-api-key',
userId: 'user-123',
videoId: 'video-456',
watermarkConfig: WatermarkConfig(
id: 'watermark-1',
type: 'static',
name: 'My Watermark',
text: 'Protected Content',
alpha: 0.8,
color: '#FFFFFF',
size: 16,
interval: 0,
skip: 0,
x: 10.0,
y: 10.0,
),
onPlay: () {},
onPause: () {},
onError: (error) {},
)
Pre-generated Session Data #
// Using pre-generated DRM session data
BmdrmVideoPlayer.drmWithSession(
drmSessionData: DrmSessionData(
edgeName: 'edge-server',
token: 'session-token',
ecdsaKey: 'signing-key',
drmServerUrl: 'https://drm-server.com/license',
),
watermarkConfig: WatermarkConfig(...),
)
Resume from Last Position #
Playback automatically resumes from where the viewer last stopped. The position
is saved on the device (Android SharedPreferences / iOS UserDefaults) as the
video plays, on pause, and when the player is closed, then restored the next time
the same video is opened. A fully watched video starts over from the beginning.
This is enabled by default. Each video is keyed by a stable resume id, resolved
in this order: the explicit resumeId, then videoId, then the videoId inside a
backend session. If none is available, resume is silently disabled for that playback.
BmdrmVideoPlayer.drmMinimal(
apiKey: 'your-api-key',
userId: 'user-123',
videoId: 'video-456', // used as the resume key when resumeId is omitted
resumeId: 'course-1/lesson-3', // optional explicit, stable key
resumeFromLastPosition: true, // default; set false to always start at 0 and skip saving
)
To forget a saved position (e.g. a "Start over" action) or read it back, use the static helpers (they don't touch any player that's currently on screen):
await VideoPlayerController.clearSavedPosition('video-456'); // forget it
final Duration last = await VideoPlayerController.getSavedPosition('video-456'); // read it
Resume is per device. Positions are not synced across devices or accounts; use the
onPositionChangedcallback if you want to persist progress to your backend.
Customizable Fullscreen Drawer #
Pass a fullscreenDrawerBuilder to show a customizable, app-owned panel beside
the video while in fullscreen — like a "Resources / Notes / Q&A" side panel. The
plugin provides the sliding panel, a toggle button and an exit-fullscreen button;
you provide the content (any Flutter widget).
Passing the builder switches fullscreen to a Flutter-rendered mode (so your widget can be drawn), and the video shrinks to sit beside the open drawer rather than behind it. When you don't pass a builder, fullscreen keeps its native behaviour, unchanged.
BmdrmVideoPlayer.drmFromBackendSession(
backendSession: session,
// Any widget you want. `controller` lets the panel close itself or leave
// fullscreen from a button inside it.
fullscreenDrawerBuilder: (context, controller) => MyLessonPanel(
onClose: controller.closeDrawer,
),
fullscreenDrawerIcon: const Icon(Icons.menu_book_rounded), // toggle button icon
fullscreenDrawerWidth: 360, // optional; defaults to ~38% of width
fullscreenDrawerAlignment: Alignment.centerRight, // or centerLeft
fullscreenDrawerBackgroundColor: Colors.white, // optional
)
Drive it programmatically (e.g. from your own button) with a
BmdrmFullscreenController:
final fsController = BmdrmFullscreenController();
BmdrmVideoPlayer.drmFromBackendSession(
backendSession: session,
fullscreenController: fsController,
fullscreenDrawerBuilder: (context, controller) => MyLessonPanel(),
);
// later:
fsController.enterFullscreen();
fsController.openDrawer();
// fsController.isFullscreen / fsController.isDrawerOpen — and it's a
// ChangeNotifier, so you can listen for changes.
The drawer is rendered by Flutter, so the player must sit under a
MaterialApp(or otherwise have anOverlay/Navigatorancestor), which is the normal case.
DRM Service Usage #
// Generate DRM session
final sessionJson = await DartDrmService.generateSession(
apiKey: 'your-api-key',
userId: 'user-123',
videoId: 'video-456',
platform: 'Android',
);
// Parse session data
final sessionData = DartDrmService.parseSessionResponse(sessionJson);
// Generate stream URLs
final drmUrls = DartDrmService.generateDrmStreamUrls(
sessionData: sessionData,
streamType: 'widevine',
quality: 'h264',
);
Configuration #
Watermark Configuration #
The WatermarkConfig class supports the following properties:
id: Unique identifier for the watermarktype: Either 'static' or 'dynamic'name: Display name for the watermarktext: The text to displayalpha: Opacity (0.0 to 1.0)color: Hex color code (e.g., '#FFFFFF')size: Font size in pixelsinterval: Animation interval in milliseconds (for dynamic watermarks)skip: Number of intervals to skipx,y: Position as percentage of video dimensions
DRM Session Data #
The DrmSessionData class contains:
edgeName: DRM edge server nametoken: Session authentication tokenecdsaKey: ECDSA signing key for URL signingdrmServerUrl: License server URLkid: Key ID (optional)certificateUrl: Certificate URL (optional)watermark: Embedded watermark configuration (optional)
Platform Support #
- Android: Uses ExoPlayer with Media3 for optimal performance
- iOS: Uses AVPlayer with FairPlay DRM support
- Web: Uses video.js with EME support (planned)
Dependencies #
The plugin requires the following dependencies in your pubspec.yaml:
dependencies:
bmdrm_mobile: ^0.0.1
Internal dependencies (managed by the plugin):
crypto: ^3.0.3http: ^1.1.0pointycastle: ^3.7.3plugin_platform_interface: ^2.0.2
Android Setup #
The plugin automatically includes the necessary Android dependencies:
androidx.media3:media3-exoplayerandroidx.media3:media3-uiandroidx.media3:media3-datasourceandroidx.media3:media3-commonandroidx.media3:media3-sessionandroidx.media3:media3-exoplayer-drm
iOS Setup #
For iOS, ensure you have the following in your ios/Podfile:
platform :ios, '12.0'
Example #
See the example/ directory for a complete working example.
Building for Production #
To build an obfuscated release:
cd example
flutter build apk --release --obfuscate --split-debug-info=build/debug-info
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing #
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Support #
For support, please open an issue on GitHub or contact the development team.