bmdrm_mobile 0.0.15 copy "bmdrm_mobile: ^0.0.15" to clipboard
bmdrm_mobile: ^0.0.15 copied to clipboard

A Flutter plugin for DRM-protected video playback with watermark support.

example/lib/main.dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:bmdrm_mobile/bmdrm_video_player.dart';
import 'package:bmdrm_mobile/dart_drm_service.dart' as drm_service;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'BMDRM Player',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const MyHomePage(),
    );
  }
}

enum DrmMethod { drmMinimal, drmWithSession, drmFromBackendSession }

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final DrmMethod _selectedMethod = DrmMethod.drmFromBackendSession;

  // Replace with your actual credentials from BMDRM dashboard
  final String _apiKey = "YOUR_API_KEY_HERE";
  final String _userId = "YOUR_USER_ID_HERE";
  final String _videoId = "YOUR_VIDEO_ID_HERE";

  // Replace with your actual pre-generated backend session JSON
  final String _preGeneratedSessionJson = '''
{"edge":"https://YOUR_EDGE_SERVER.video-crypt.com","videoId":"YOUR_VIDEO_ID","signature":"YOUR_SIGNATURE","userId":"YOUR_USER_ID","subscriptionId":"YOUR_SUBSCRIPTION_ID","sessionId":"YOUR_SESSION_ID","nonce":"YOUR_NONCE","date":0}
''';

  // Replace with your actual DRM session data
  final String _drmSessionDataJson = '''
{
  "edgeName": "https://your-edge-server.com",
  "token": "your-session-token",
  "ecdsaKey": "your-ecdsa-key",
  "drmServerUrl": "https://your-license-server.com/license"
}
''';

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    final videoHeight = screenWidth * 9 / 16;

    return Scaffold(
      backgroundColor: Colors.red,

      body: SafeArea(
        child: Column(
          children: [
            // Method selector

            // Player
            Expanded(child: Center(child: _buildPlayer(videoHeight))),
          ],
        ),
      ),
    );
  }

  Widget _buildPlayer(double videoHeight) {
    try {
      switch (_selectedMethod) {
        case DrmMethod.drmMinimal:
          return _buildDrmMinimalPlayer(videoHeight);
        case DrmMethod.drmWithSession:
          return _buildDrmWithSessionPlayer(videoHeight);
        case DrmMethod.drmFromBackendSession:
          return _buildDrmFromBackendSessionPlayer(videoHeight);
      }
    } catch (e) {
      return Container(
        width: double.infinity,
        height: videoHeight,
        color: Colors.black,
        child: Center(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text(
              'Error: $e',
              style: const TextStyle(color: Colors.red),
              textAlign: TextAlign.center,
            ),
          ),
        ),
      );
    }
  }

  Widget _buildDrmMinimalPlayer(double videoHeight) {
    return BmdrmVideoPlayer.drmMinimal(
      apiKey: _apiKey,
      userId: _userId,
      videoId: _videoId,
      watermarkConfig: null,
      width: double.infinity,
      height: videoHeight,
      autoPlay: true,
      onError: (error) {
        debugPrint('DRM Minimal Error: $error');
      },
      onPlay: () {
        debugPrint('DRM Minimal: Video started playing');
      },
      onPause: () {
        debugPrint('DRM Minimal: Video paused');
      },
    );
  }

  Widget _buildDrmWithSessionPlayer(double videoHeight) {
    try {
      final sessionMap = jsonDecode(_drmSessionDataJson);
      final drmSessionData = drm_service.DrmSessionData.fromJson(sessionMap);

      return BmdrmVideoPlayer.drmWithSession(
        drmSessionData: drmSessionData,
        watermarkConfig: null,
        width: double.infinity,
        height: videoHeight,
        autoPlay: true,
        onError: (error) {
          debugPrint('DRM With Session Error: $error');
        },
        onPlay: () {
          debugPrint('DRM With Session: Video started playing');
        },
        onPause: () {
          debugPrint('DRM With Session: Video paused');
        },
      );
    } catch (e) {
      return Container(
        width: double.infinity,
        height: videoHeight,
        color: Colors.black,
        child: Center(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text(
              'Error parsing session data: $e\n\nPlease update _drmSessionDataJson with valid session data.',
              style: const TextStyle(color: Colors.red),
              textAlign: TextAlign.center,
            ),
          ),
        ),
      );
    }
  }

  Widget _buildDrmFromBackendSessionPlayer(double videoHeight) {
    try {
      final sessionMap = jsonDecode(_preGeneratedSessionJson);
      return BmdrmVideoPlayer.drmFromBackendSession(
        backendSession: sessionMap,
        watermarkConfig: null,
        width: double.infinity,
        height: videoHeight,
        autoPlay: true,
        // Last-position resume. This is ON by default; playback automatically
        // resumes where the viewer left off and the position is saved on the
        // device as they watch. The key falls back to the session's videoId, so
        // `resumeId` here is optional — pass it to use your own stable key.
        resumeId: _videoId,
        resumeFromLastPosition: true,
        // ── Customizable fullscreen drawer ──────────────────────────────────
        // Passing a builder turns on the Flutter-rendered fullscreen: while in
        // fullscreen a toggle button opens this panel beside the video. The app
        // owns the content entirely — put any widgets here.
        fullscreenDrawerIcon: const Icon(Icons.menu_book_rounded),
        fullscreenDrawerBuilder: (context, controller) =>
            _AboutDrawer(onClose: controller.closeDrawer),
        onError: (error) {
          debugPrint('DRM From Backend Session Error: $error');
        },
        onPlay: () {
          debugPrint('DRM From Backend Session: Video started playing');
        },
        onPause: () {
          debugPrint('DRM From Backend Session: Video paused');
        },
      );
    } catch (e, stack) {
      debugPrint('Error parsing backend session: $e');
      debugPrint('Stack trace: $stack');
      return Container(
        width: double.infinity,
        height: videoHeight,
        color: Colors.black,
        child: Center(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text(
              'Error parsing backend session: $e',
              style: const TextStyle(color: Colors.red),
              textAlign: TextAlign.center,
            ),
          ),
        ),
      );
    }
  }
}

/// Example content for the fullscreen drawer: a tabbed "À propos" panel similar
/// to the reference design. This lives entirely in the host app — the plugin
/// only provides the sliding panel and the toggle button.
class _AboutDrawer extends StatelessWidget {
  const _AboutDrawer({required this.onClose});

  final VoidCallback onClose;

  @override
  Widget build(BuildContext context) {
    const tabs = ['Ressources', 'Description', 'Q&R', 'Notes'];
    return DefaultTabController(
      length: tabs.length,
      initialIndex: tabs.length - 1,
      child: SafeArea(
        left: false,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(16, 12, 8, 4),
              child: Row(
                children: [
                  const Expanded(
                    child: Text(
                      'À propos',
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.w700,
                      ),
                    ),
                  ),
                  IconButton(
                    icon: const Icon(Icons.close_rounded),
                    onPressed: onClose,
                  ),
                ],
              ),
            ),
            TabBar(
              isScrollable: true,
              tabAlignment: TabAlignment.start,
              tabs: [for (final t in tabs) Tab(text: t)],
            ),
            const Expanded(
              child: TabBarView(
                children: [
                  _DrawerPlaceholder(icon: Icons.folder_outlined, label: 'Ressources'),
                  _DrawerPlaceholder(icon: Icons.description_outlined, label: 'Description'),
                  _DrawerPlaceholder(icon: Icons.forum_outlined, label: 'Questions & réponses'),
                  _DrawerPlaceholder(
                    icon: Icons.sticky_note_2_outlined,
                    label: 'Appuyez sur "+" pour écrire votre première remarque',
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _DrawerPlaceholder extends StatelessWidget {
  const _DrawerPlaceholder({required this.icon, required this.label});

  final IconData icon;
  final String label;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(icon, size: 40, color: Colors.grey),
            const SizedBox(height: 12),
            Text(
              label,
              textAlign: TextAlign.center,
              style: const TextStyle(color: Colors.grey),
            ),
          ],
        ),
      ),
    );
  }
}
2
likes
0
points
896
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for DRM-protected video playback with watermark support.

Homepage

License

unknown (license)

Dependencies

crypto, flutter, http, plugin_platform_interface, pointycastle

More

Packages that depend on bmdrm_mobile

Packages that implement bmdrm_mobile