Thanks to visit codestin.com
Credit goes to github.com

Skip to content

1. Enabled HDR , App crashed> #311

@YongNam-dev

Description

@YongNam-dev

Describe the bug

  1. Possible to force HDR on all Video that are SDR only
  2. Will HDR content Work, if Video is HDR.

Expected behavior

  1. Main
  // set logger before registerWith()
  l.Logger.root.level = l.Level.ALL;
  final df = DateFormat("HH:mm:ss.SSS");
  l.Logger.root.onRecord.listen((record) {
    debugPrint(
      '${record.loggerName}.${record.level.name}: ${df.format(record.time)}: ${record.message}',
      wrapWidth: 0x7FFFFFFFFFFFFFFF,
    );
  });

  final opts = <String, Object>{};
  final globalOpts = <String, Object>{};
  int i = 0;
  bool useFvp = true;

  for (; i < args.length; i++) {
    if (args[i] == '-c:v') {
      opts['video.decoders'] = [args[++i]];
    } else if (args[i] == '-maxSize') {
      // ${w}x${h}
      final size = args[++i].split('x');
      opts['maxWidth'] = int.parse(size[0]);
      opts['maxHeight'] = int.parse(size[1]);
    } else if (args[i] == '-fvp') {
      useFvp = int.parse(args[++i]) > 0;
    } else if (args[i].startsWith('-')) {
      globalOpts[args[i].substring(1)] = args[++i];
    } else {
      break;
    }
  }
  if (globalOpts.isNotEmpty) {
    opts['global'] = globalOpts;
  }
  opts['lowLatency'] = 0;

  if (i <= args.length - 1) source = args[args.length - 1];

  mdk.setGlobalOption("audio.backends", "AudioTrack");
  mdk.setGlobalOption("audio.openSL", "no");

  if (useFvp) {
    fvp.registerWith(
      options: {
        // Subtitle fully disabled (you already confirmed this works)
        'subtitle.enabled': 'no',
        'subtitle.auto': 'no',
        'subtitle.default': 'no',
        'subtitle.preferred': '',
        'ffmpeg.enable.all': '1',
        // Prefer hardware decoders that preserve 10-bit
        'video.decoders': [
          'D3D11VA',
          'NVDEC',
          'VAAPI',
          'VideoToolbox',
          'AMediaCodec',
          'FFmpeg',
        ],
        // HDR + 10-bit + Tone Mapping + Metadata Passthrough
        'videoout.hdr': 'yes', // Master HDR switch
        'videoout.hdr10_metadata':
            'yes', // Send HDR10/HDR10+ metadata to display
        'videoout.tone_mapping':
            'hable', // Best perceptual tone mapper (or 'mobius', 'reinhard')
        'videoout.tone_mapping.mode': 'auto', // Auto: HDR→SDR when needed
        'videoout.color_space':
            'auto', // Let MDK detect BT.709 / BT.2020 / DCI-P3
        'videoout.color_transfer': 'auto',
        // Optional: For low-latency HDR streaming
        'lowLatency': 1,
      },
    );
  }
  1. PlaybackProvider
import 'dart:io';
import 'dart:math';
import 'package:beplayed/database.dart' as db;
import 'package:beplayed/models/media_asset.dart';
import 'package:beplayed/models/playback_position.dart';
import 'package:beplayed/providers/dynamic_seed_color_provider.dart';
import 'package:beplayed/providers/media_provider.dart';
import 'package:beplayed/providers/queue_provider.dart';
import 'package:beplayed/providers/settings_provider.dart';
import 'package:beplayed/services/audio_service_handler.dart';
import 'package:beplayed/services/preferences_service.dart';
import 'package:beplayed/widgets/player/subtitle_controller.dart';
import 'package:flutter/material.dart';
import 'package:fvp/fvp.dart' show FVPControllerExtensions;
import 'package:just_audio/just_audio.dart';
import 'package:logger/logger.dart';
import 'package:video_player/video_player.dart';
import 'package:provider/provider.dart';
import 'package:synchronized/synchronized.dart';
import 'package:wakelock_plus/wakelock_plus.dart';

import '../enum/repeat_mode.dart';

final logger = Logger();

class PlaybackProvider with ChangeNotifier, WidgetsBindingObserver {
  double _currentPosition = 0.0;
  double _duration = 0.0;
  late BePlayedAudioHandler? audioHandler;
  late final AudioPlayer _audioPlayer;
  VideoPlayerController? _videoController;
  bool _videoPlayerInitialized = false;
  MediaAsset? _currentMedia;
  MediaAsset? _nextMedia;
  bool _isPlaying = false;
  bool _isDurationConfirmed = false;
  bool _isLoading = false;
  BoxFit _videoFit = BoxFit.contain;
  String _videoFitLabel = 'Fit';
  double _volume = 1.0;
  bool _isMuted = false;
  double _previousVolume = 1.0;
  final db.AppDatabase _db;
  late final MediaProvider mediaProvider;
  late final QueueProvider _queue;
  final _lock = Lock();
  Timer? _debounceTimer;
  bool _pendingNotify = false;
  RepeatMode _repeatMode = RepeatMode.none;
  RepeatMode get repeatMode => _repeatMode;
  bool _shuffle = false;
  bool _ignoreNextCompletion = false;
  int? audioColdStartPosition;
  bool isRepeatingOnce = false;

  final ValueNotifier<double> aspectRatio = ValueNotifier<double>(16 / 9);
  final ValueNotifier<double> positionListenable = ValueNotifier<double>(0.0);
  final ValueNotifier<bool> isPlayingListenable = ValueNotifier<bool>(false);
  final ValueNotifier<BoxFit> videoFitListenable = ValueNotifier<BoxFit>(
    BoxFit.contain,
  );

  StreamSubscription? _audioStateSub;
  StreamSubscription? _audioPositionSub;
  StreamSubscription? _videoPositionSub;
  StreamSubscription? _videoDurationSub;
  StreamSubscription? _videoPlayingSub;

  VoidCallback? _completionCallback;

  void Function()? _videoPositionListener;
  void Function()? _videoDurationListener;
  void Function()? _videoPlayingListener;

  final List<VoidCallback> _completionListeners = [];

  VoidCallback addCompletionListener(VoidCallback listener) {
    _completionListeners.add(listener);
    return () => _completionListeners.remove(listener);
  }

  void _notifyCompletion() {
    for (final listener in List<VoidCallback>.from(_completionListeners)) {
      listener();
    }
  }

  VideoPlayerController? get videoController => _videoController;
  VideoPlayerController? get videoControllerIfReady =>
      _videoPlayerInitialized ? _videoController : null;

  Future<void> setRepeatMode(RepeatMode mode) async {
    if (_repeatMode == mode) return;
    _repeatMode = mode;
    await _save('playback_repeat_mode', mode.index);
    logger.i('Repeat mode → $mode');
    notifyListeners();
  }

  double _validatePosition(double position, {bool rejectInvalid = false}) {
    if (_duration <= 0 || !_isDurationConfirmed) {
      if (rejectInvalid) {
        logger.w('Cannot seek: duration not confirmed ($_duration)');
        return 0.0;
      }
      return position.clamp(0.0, 12 * 60 * 60.0); // fallback cap
    }
    return position.clamp(0.0, _duration);
  }

  PlaybackProvider(BePlayedAudioHandler? handler, this._db, this._queue)
    : _audioPlayer = AudioPlayer(
        handleInterruptions: true,
        androidApplyAudioAttributes: true,
        audioLoadConfiguration: AudioLoadConfiguration(
          androidLoadControl: AndroidLoadControl(
            minBufferDuration: const Duration(milliseconds: 5000),
            maxBufferDuration: const Duration(milliseconds: 15000),
            bufferForPlaybackDuration: const Duration(milliseconds: 2000),
            bufferForPlaybackAfterRebufferDuration: const Duration(
              milliseconds: 3000,
            ),
          ),
        ),
      ) {
    WidgetsBinding.instance.addObserver(this);
    _loadAllPreferences();
    _setupListeners();
    _setupAppLifecycleListener();
    audioHandler = handler;
  }

  final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  Future<void> _loadAllPreferences() async {
    final p = PreferencesService();

    // Repeat
    final repeatIdx =
        p.get<int>('playback_repeat_mode') ?? RepeatMode.none.index;
    _repeatMode = RepeatMode.values[repeatIdx];

    // Shuffle
    _shuffle = p.get<bool>('playback_shuffle') ?? false;

    // Video fit
    final fit = p.get<String>('video_fit') ?? 'contain';
    _videoFit = switch (fit) {
      'none' => BoxFit.none,
      'contain' => BoxFit.contain,
      'cover' => BoxFit.cover,
      _ => BoxFit.contain,
    };
    _videoFitLabel = switch (_videoFit) {
      BoxFit.none => 'Original',
      BoxFit.contain => 'Fit',
      BoxFit.cover => 'Fill',
      _ => 'Fit',
    };
    videoFitListenable.value = _videoFit;

    // Volume / mute
    _volume = p.get<double>('volume') ?? 1.0;
    _isMuted = p.get<bool>('is_muted') ?? false;
    _previousVolume = _volume > 0 ? _volume : 1.0;
    await _applyVolume();

    logger.i(
      'PlaybackProvider – loaded prefs → repeat=$_repeatMode, '
      'shuffle=$_shuffle, fit=$_videoFitLabel, vol=$_volume, muted=$_isMuted',
    );

    notifyListeners();
  }

  Future<void> _save<T>(String key, T value) =>
      PreferencesService().save(key, value);

  void initializeMediaProvider(MediaProvider provider) {
    mediaProvider = provider;
  }

  // Getters unchanged
  MediaAsset? get nextMedia => _nextMedia;
  MediaAsset? get currentMedia => _currentMedia;
  bool get isPlaying => _isPlaying;
  double get currentPosition => _currentPosition;
  double get duration => _duration;
  AudioPlayer? get audioPlayer => _audioPlayer;
  bool get isVideo => _currentMedia?.mediaType == 'video';
  bool get isDurationConfirmed => _isDurationConfirmed;
  double get percentage => _duration > 0 ? _currentPosition / _duration : 0.0;
  bool get isLoading => _isLoading;
  BoxFit get videoFit => _videoFit;
  String get videoFitLabel => _videoFitLabel;
  double get volume => _volume;
  bool get isMuted => _isMuted;

  void _debouncedNotify() {
    if (_debounceTimer?.isActive ?? false) return;
    _debounceTimer = Timer(Duration(milliseconds: 100), () {
      if (_pendingNotify) {
        notifyListeners();
        _pendingNotify = false;
      }
    });
    _pendingNotify = true;
  }

  void _setupAppLifecycleListener() {
    logger.i('PlaybackProvider: App lifecycle listener initialized');
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused && isVideo && _isPlaying) {
      logger.i('App minimized → video paused (normal behavior)');
      // Optional: show toast or update UI
      // "Video paused. Tap notification to resume."
    }
  }

  void setPlaying(bool isPlaying) {
    if (_isPlaying != isPlaying) {
      _isPlaying = isPlaying;
      isPlayingListenable.value = isPlaying;
    }
  }

  void setPosition(double position) {
    final clamped = _duration > 0 ? position.clamp(0.0, _duration) : position;
    if ((_currentPosition - clamped).abs() > 0.05) {
      _currentPosition = clamped;
      positionListenable.value = clamped;
    }
  }

  void setDuration(double durationSeconds) {
    final playerDuration = durationSeconds;
    final metadataDuration = _currentMedia?.duration?.toDouble() ?? 0.0;

    // Prefer player duration if stable and close to metadata
    double finalDuration = playerDuration;
    if (metadataDuration > 0 &&
        (playerDuration - metadataDuration).abs() < 5.0) {
      finalDuration = max(
        playerDuration,
        metadataDuration,
      ); // Avoid rounding down
    } else if (playerDuration <= 0 && metadataDuration > 0) {
      finalDuration = metadataDuration;
    }

    if ((_duration - finalDuration).abs() > 0.1 || !_isDurationConfirmed) {
      _duration = finalDuration;
      _isDurationConfirmed = true;
      _currentPosition = _currentPosition.clamp(0.0, _duration);
      notifyListeners(); // ← Only for duration change (rare)
    }
  }

  void setLoading(bool loading) {
    if (_isLoading != loading) {
      _isLoading = loading;
      // logger.i('PlaybackProvider: setLoading=$loading');
      _debouncedNotify();
    }
  }

  void _setupListeners() {
    _setupAudioListeners();
    _setupVideoListeners();
  }

  Future<void> _handlePlayerError() async {
    // Cancel all subscriptions
    _audioStateSub?.cancel();
    _audioPositionSub?.cancel();
    _videoPositionSub?.cancel();
    _videoDurationSub?.cancel();
    _videoPlayingSub?.cancel();

    logger.i('PlaybackProvider: Handling player error');

    _isPlaying = false;
    _isLoading = false;
    _currentPosition = 0.0;
    _duration = 0.0;
    _isDurationConfirmed = false;

    try {
      await _audioPlayer.stop();
      await _audioPlayer.seek(Duration.zero);

      // NEW: Clean up video_player controller
      if (_videoController != null) {
        await _videoController!.pause();
        await _videoController!.seekTo(Duration.zero);
        await _videoController!.dispose();
        _videoController = null;
        _videoPlayerInitialized = false;
      }

      // Re-setup listeners (they will be re-attached on next play)
      _setupAudioListeners();
      // Video listeners are set up only when a video starts → no need here

      _currentMedia = null;
      _nextMedia = null;
    } catch (e, stackTrace) {
      logger.e(
        'Failed to recover from error: $e',
        error: e,
        stackTrace: stackTrace,
      );

      // Extra safety: make sure video controller is dead
      try {
        await _videoController?.dispose();
      } catch (_) {}
      _videoController = null;
      _videoPlayerInitialized = false;

      _currentMedia = null;
      _nextMedia = null;
    }

    notifyListeners();
  }

  void _setupVideoListeners() {
    // Cancel old listeners
    if (_videoController != null) {
      _videoController!.removeListener(_videoPositionListener ?? () {});
      _videoController!.removeListener(_videoDurationListener ?? () {});
      _videoController!.removeListener(_videoPlayingListener ?? () {});
    }

    _videoPositionListener = null;
    _videoDurationListener = null;
    _videoPlayingListener = null;

    if (_videoController == null) return;

    // ───── Position + Completion ─────
    _videoPositionListener = () {
      if (!_videoController!.value.isInitialized || _isLoading) return;

      double pos = _videoController!.value.position.inSeconds.toDouble();

      // Snap to end
      if (_isDurationConfirmed &&
          _duration > 0 &&
          (_duration - pos).abs() <= 1.0 &&
          pos < _duration) {
        pos = _duration;
      }

      final clamped = _duration > 0 ? pos.clamp(0.0, _duration) : pos;
      if ((_currentPosition - clamped).abs() > 0.05) {
        _currentPosition = clamped;
        positionListenable.value = clamped;
      }

      // Completion detection
      if (_videoController!.value.position >=
              _videoController!.value.duration &&
          _videoController!.value.duration > Duration.zero &&
          !_ignoreNextCompletion &&
          !isRepeatingOnce) {
        logger.i('FVP VIDEO COMPLETED – notifying listeners');
        _notifyCompletion();
      }
    };

    // ───── Duration ─────
    _videoDurationListener = () {
      final dur = _videoController!.value.duration;
      if (dur > Duration.zero) {
        setDuration(dur.inSeconds.toDouble());
        setLoading(false);
      }
    };

    // ───── Playing state ─────
    _videoPlayingListener = () {
      final playing = _videoController!.value.isPlaying;
      if (_isPlaying != playing && !_isLoading) {
        _isPlaying = playing;
        isPlayingListenable.value = playing;
      }
    };

    // Register all three
    _videoController!.addListener(_videoPositionListener!);
    _videoController!.addListener(_videoDurationListener!);
    _videoController!.addListener(_videoPlayingListener!);
  }

  // NEW: Handle aspect ratio from video_player
  void _updateAspectRatio() {
    if (_videoController?.value.isInitialized != true) return;

    final size = _videoController!.value.size;
    if (size.width > 0 && size.height > 0) {
      final newRatio = size.width / size.height;
      if (newRatio.isFinite && newRatio != aspectRatio.value) {
        aspectRatio.value = newRatio;
        logger.d('Aspect ratio updated: $newRatio (from video_player)');
      }
    }
  }

  void _setupAudioListeners() {
    _audioStateSub?.cancel();
    _audioPositionSub?.cancel();

    DateTime? lastPositionUpdate;
    const throttleDuration = Duration(milliseconds: 200);

    // POSITION STREAM: Snap to end + force 100%
    _audioPlayer.positionStream.listen((position) {
      final now = DateTime.now();

      if (!_isLoading &&
          (lastPositionUpdate == null ||
              now.difference(lastPositionUpdate!) > throttleDuration)) {
        double pos = position.inSeconds.toDouble();

        if (_isDurationConfirmed &&
            _duration > 0 &&
            (_duration - pos) <= 1.0 &&
            (_duration - pos) > 0) {
          pos = _duration;
        }

        final clamped = _duration > 0 ? pos.clamp(0.0, _duration) : pos;
        if ((_currentPosition - clamped).abs() > 0.05) {
          _currentPosition = clamped;
          positionListenable.value = clamped;
          lastPositionUpdate = now;
        }
      }
    });

    // DURATION STREAM: Trust player
    _audioPlayer.durationStream.listen((duration) {
      if (duration != null && duration.inSeconds > 0) {
        final playerSec = duration.inSeconds.toDouble();
        if (!_isDurationConfirmed || (playerSec - _duration).abs() > 0.5) {
          setDuration(playerSec);
        }
        if (_isLoading &&
            _audioPlayer.processingState == ProcessingState.ready) {
          setLoading(false);
        }
      }
    });

    // STATE STREAM: Reliable completion
    _audioPlayer.playerStateStream.listen((state) async {
      if (_isLoading || _isPlaying != state.playing) {
        _isPlaying = state.playing;
        _isLoading =
            state.processingState == ProcessingState.loading ||
            state.processingState == ProcessingState.buffering;
        notifyListeners();
      }

      if (_isLoading &&
          state.processingState == ProcessingState.idle &&
          _currentMedia != null) {
        logger.w('Audio stuck in idle → retry');
        _handlePlayerError();
      }

      if (state.processingState == ProcessingState.ready && _isLoading) {
        setLoading(false);
        if (!_isPlaying && _currentMedia != null && !state.playing) {
          _audioPlayer.play().catchError((e, s) {
            logger.e('Auto-play failed', error: e, stackTrace: s);
            _handlePlayerError();
          });
        }
      }

      if (state.processingState == ProcessingState.completed &&
          _currentMedia != null) {
        if (_ignoreNextCompletion || isRepeatingOnce) {
          logger.i('IGNORED COMPLETION: repeatOnce replay in progress');
          isRepeatingOnce = false; // Reset
          return;
        }

        final atEnd =
            _isDurationConfirmed &&
            _duration > 0 &&
            (_duration - _currentPosition).abs() < 2.0;

        if (atEnd) {
          logger.i('AUDIO COMPLETED – waiting for QueueProvider');
          _completionCallback?.call();
        }
      }
    });
  }

  Future<void> playMedia(
    BuildContext context,
    MediaAsset media, {
    bool autoPlay = true,
    bool forcePositionZero = false,
  }) async {
    logger.i(
      'PlaybackProvider: Preparing ${media.filename}, filePath: ${media.filePath}, autoPlay: $autoPlay, media.duration: ${media.duration}',
    );

    final subtitleController = Provider.of<SubtitleController>(
      context,
      listen: false,
    );

    await subtitleController.loadEmbeddedSubtitles(context, media);
    await subtitleController.loadAudioTracks(media);
    await subtitleController.loadSavedSubtitle(context, media);

    final settings = Provider.of<SettingsProvider>(context, listen: false);

    if (subtitleController.currentTrackId == null &&
        !subtitleController.hasManualOverride &&
        settings.enableSubtitle) {
      await subtitleController.autoApplySubtitleRules(context, media);
    }

    try {
      context.read<DynamicSeedColorProvider>().updateFromCurrentMedia(media);
    } catch (_) {
      // Ignore: happens on first launch or during disposal
    }

    if (_currentMedia?.filePath == media.filePath &&
        _isPlaying &&
        !_isLoading &&
        autoPlay &&
        !isRepeatingOnce) {
      logger.i('PlaybackProvider: Media already playing');
      return;
    }

    await saveCurrentPosition();
    await _stopAllPlayers();

    _currentMedia = media;

    _isPlaying = false;
    final isVideo = media.mediaType == 'video';

    if (isVideo) {
      WakelockPlus.enable();
    }

    int startPosition = 0;

    if (forcePositionZero) {
      startPosition = 0;
      logger.i('FORCE POSITION = 0 (repeatOnce replay)');
    } else {
      if (media.mediaType == 'video' && settings.enableResume) {
        final lastPos = await mediaProvider.getLastPosition(media) ?? 0;
        startPosition = settings.resumeMode == 'always' ? lastPos : 0;
      } else {
        startPosition = 0;
      }
    }

    if (!isVideo) {
      audioColdStartPosition = null; // One-time use
    }

    // INIT DURATION FROM METADATA
    _duration = 0.0;
    _isDurationConfirmed = false;
    if (media.duration != null && media.duration! > 0) {
      setDuration(media.duration!.toDouble());
    }

    _currentPosition = _validatePosition(startPosition.toDouble());
    _isLoading = true;

    notifyListeners();

    try {
      if (isRepeatingOnce) {
        isRepeatingOnce = false;
        logger.i('REPEAT ONCE: Replay started → flag reset');
      }

      if (isVideo) {
        _videoController = VideoPlayerController.file(File(media.filePath));
        // OR for network: VideoPlayerController.networkUrl(Uri.parse(url))

        await _videoController!.initialize();
        _videoPlayerInitialized = true;

        try {
          if (_videoController!.value.isInitialized) {
            _videoController!.setSubtitleTracks([]);
            _videoController!.setProperty('videoout.hdr', 'yes');
            _videoController!.setProperty('videoout.tone_mapping', 'hable');
          }
        } catch (e) {
          logger.w('FVP HDR/subtitle setup failed: $e');
        }

        _videoPlayerInitialized = true;

        subtitleController.ensureAttached();

        _setupVideoListeners();
        _updateAspectRatio();

        await _videoController!.setVolume(_volume);
        await _videoController!.seekTo(Duration(seconds: startPosition));
        if (autoPlay) await _videoController!.play();

        _isPlaying = autoPlay;
      } else {
        await _audioPlayer.setFilePath(media.filePath);
        await _audioPlayer.seek(Duration(seconds: startPosition));
        await _audioPlayer.setVolume(_volume);
        if (autoPlay) {
          if (_audioPlayer.processingState == ProcessingState.ready ||
              _audioPlayer.processingState == ProcessingState.completed) {
            await _audioPlayer.play();
          } else {
            // Let playerStateStream handle auto-play when ready
            logger.i('Auto-play deferred: player not ready yet');
          }
        }
        _isPlaying = autoPlay ? (_audioPlayer.playing) : false;
      }
      _isLoading = false;
      audioHandler?.syncCurrentMedia();
      // logger.i(
      //   'PlaybackProvider: Successfully prepared playback from position $lastPos, isPlaying: $_isPlaying, duration: $_duration',
      // );
    } catch (e, stackTrace) {
      logger.e(
        'PlaybackProvider: Error preparing media: $e',
        error: e,
        stackTrace: stackTrace,
      );
      await _handlePlayerError();
    } finally {
      notifyListeners();
    }
  }

  /// 🔇 SILENT: Same as stop() but NO notifyListeners()
  void stopSilent() {
    _stopAllPlayers();
    _currentMedia = null;
    _currentPosition = 0.0;
    _duration = 0.0;
    _isLoading = false;
    logger.i('🔇 SILENT: Stopped playback');
    // 🔥 NO notifyListeners()
  }

  void setPositionSilent(double position) {
    _currentPosition = _duration > 0
        ? position.clamp(0.0, _duration)
        : position;
    // 🔥 NO notifyListeners()
  }

  Future<void> _stopAllPlayers() async {
    await _lock.synchronized(() async {
      _ignoreNextCompletion = true;
      try {
        await _audioPlayer.stop();
        await _audioPlayer.seek(Duration.zero);

        if (_videoController != null) {
          await _videoController!.pause();
          await _videoController!.seekTo(Duration.zero);
          _videoController!.removeListener(() {}); // Clear old listeners
          await _videoController!.dispose();
          _videoController = null;
          _videoPlayerInitialized = false;
        }
        _isPlaying = false;
        notifyListeners();
      } catch (e, s) {
        logger.e('Error stopping players', error: e, stackTrace: s);
      } finally {
        Timer(
          const Duration(milliseconds: 800),
          () => _ignoreNextCompletion = false,
        );
      }
    });
  }

  Future<void> pause() async {
    if (_isLoading || !_isPlaying) return;
    _ignoreNextCompletion = true;

    try {
      if (isVideo && _videoController != null) {
        await _videoController!.pause();
      } else {
        await _audioPlayer.pause();
      }
      _isPlaying = false;
      await audioHandler?.pause();
      await saveCurrentPosition(sync: true);
    } catch (e, s) {
      logger.e('Pause error', error: e, stackTrace: s);
    } finally {
      Timer(
        const Duration(milliseconds: 800),
        () => _ignoreNextCompletion = false,
      );
      notifyListeners();
    }
  }

  Future<void> resume() async {
    if (_currentMedia == null) return;

    if (isVideo && _videoController != null) {
      if (_videoController!.value.isPlaying) return;
      await _videoController!.play();
      _isPlaying = true;
      notifyListeners();
      return;
    }

    if (_audioPlayer.playing) return;
    await _audioPlayer.play();
    _isPlaying = true;
    notifyListeners();
  }

  Future<void> stop() async {
    final wasVideo = isVideo;
    if (!wasVideo) {
      _resetAudioSession();
    }
    await _stopAllPlayers();
    WakelockPlus.disable();
    _currentMedia = null;
    _currentPosition = 0.0;
    _duration = 0.0;
    _isLoading = false;
    logger.i('PlaybackProvider: Stopped playback');
    notifyListeners();
  }

  void _resetAudioSession() {
    audioColdStartPosition = null;
  }

  Future<void> seek(double position) async => _seekInternal(position);

  Future<void> seekTo(Duration position) async =>
      _seekInternal(position.inSeconds.toDouble());

  Future<void> _seekInternal(double positionSeconds) async {
    final clamped = _validatePosition(positionSeconds, rejectInvalid: true);
    if (clamped == 0.0 && positionSeconds > 0) return;

    // IMMEDIATELY update UI (critical for smooth scrubbing!)
    _currentPosition = clamped;
    positionListenable.value = clamped;
    notifyListeners();

    try {
      if (isVideo && _videoController != null && _videoPlayerInitialized) {
        // This is the magic: seek + play in one go
        await _videoController!.seekTo(Duration(seconds: clamped.toInt()));

        // Force play even if it was paused — this wakes up the player
        if (!_videoController!.value.isPlaying) {
          await _videoController!.play();
        }

        // Optional: small delay to ensure frame appears (feels snappier)
        await Future.delayed(const Duration(milliseconds: 50));
        if (_isPlaying) {
          await _videoController!.play(); // ensure it's really playing
        }
      } else {
        await _audioPlayer.seek(Duration(seconds: clamped.toInt()));
        if (_isPlaying) {
          await _audioPlayer.play();
        }
      }
    } catch (e, s) {
      logger.e('Seek failed', error: e, stackTrace: s);
    }
  }

  Future<void> saveCurrentPosition({bool sync = false}) async {
    if (_currentMedia != null) {
      final savePosition = _duration > 0
          ? _currentPosition.clamp(0.0, _duration)
          : _currentPosition;
      if (sync) {
        await _db.transaction(() async {
          final existing =
              await (_db.select(_db.playbackPositions)..where(
                    (tbl) => tbl.filePath.equals(_currentMedia!.filePath),
                  ))
                  .getSingleOrNull();
          final playbackPosition = PlaybackPosition(
            id: existing?.id,
            filePath: _currentMedia!.filePath,
            position: savePosition.toInt(),
            mediaType: _currentMedia!.mediaType ?? 'unknown',
            lastUpdated: DateTime.now(),
          );
          await _db
              .into(_db.playbackPositions)
              .insertOnConflictUpdate(playbackPosition.toCompanion());
        });
        // logger.d(
        //   'PlaybackProvider: Synchronously saved position ${savePosition.toInt()} for ${_currentMedia!.filename}',
        // );
      } else {
        await mediaProvider.setLastPosition(
          _currentMedia!,
          savePosition.toInt(),
        );
        logger.d(
          'PlaybackProvider: Saved position ${savePosition.toInt()} for ${_currentMedia!.filename}',
        );
      }
    }
  }

  Future<void> onCompletion(BuildContext context) async {
    if (_currentMedia == null) return;

    await saveCurrentPosition(sync: true);
    await mediaProvider.setLastPosition(_currentMedia!, 0);

    if (repeatMode == RepeatMode.repeatOnce) {
      isRepeatingOnce = true;
      await mediaProvider.initiatePlayback(
        _currentMedia!,
        context,
        autoPlay: true,
        forcePositionZero: true,
      );
      logger.i('REPEAT ONCE → replaying ${_currentMedia!.filename}');
      return;
    }

    // ALWAYS let QueueProvider handle next
    await _queue.onPlaybackCompleted(
      _currentMedia!,
      mediaProvider,
      context,
      repeatMode,
    );

    audioHandler?.syncCurrentMedia();
  }

  void setVideoFit(BoxFit fit) async {
    if (_videoFit == fit) return;
    _videoFit = fit;
    _videoFitLabel = switch (fit) {
      BoxFit.none => 'Original',
      BoxFit.contain => 'Fit',
      BoxFit.cover => 'Fill',
      _ => 'Fit',
    };
    videoFitListenable.value = fit;
    await _save('video_fit', switch (fit) {
      BoxFit.none => 'none',
      BoxFit.contain => 'contain',
      BoxFit.cover => 'cover',
      _ => 'contain',
    });
    logger.i('Video fit → $_videoFitLabel');
  }

  void cycleVideoFit() {
    final order = [BoxFit.none, BoxFit.contain, BoxFit.cover];
    final idx = (order.indexOf(_videoFit) + 1) % order.length;
    setVideoFit(order[idx]);
  }

  Future<void> _applyVolume() async {
    try {
      if (isVideo) {
        await _videoController!.setVolume(_isMuted ? 0 : _volume);
      } else {
        await _audioPlayer.setVolume(_volume);
      }
    } catch (e, s) {
      logger.e('Failed to apply volume', error: e, stackTrace: s);
    }
  }

  void setVolume(double newVolume) {
    newVolume = newVolume.clamp(0.0, 1.0);
    if (_volume == newVolume) return;

    _volume = newVolume;
    _isMuted = newVolume == 0.0;
    if (_volume > 0) _previousVolume = _volume;

    _applyVolume();
    _save('volume', _volume);
    _save('is_muted', _isMuted);
    logger.i('Volume → $_volume (muted=$_isMuted)');
    notifyListeners();
  }

  void toggleMute() {
    if (_isMuted) {
      _volume = _previousVolume > 0 ? _previousVolume : 1.0;
      _isMuted = false;
    } else {
      _previousVolume = _volume;
      _volume = 0.0;
      _isMuted = true;
    }
    _applyVolume();
    _save('volume', _volume);
    _save('is_muted', _isMuted);
    logger.i('Mute toggled → $_isMuted');
    notifyListeners();
  }

  @override
  void dispose() {
    _completionListeners.clear();
    WakelockPlus.disable();
    _ignoreNextCompletion = false;
    WidgetsBinding.instance.removeObserver(this);

    _audioStateSub?.cancel();
    _audioPositionSub?.cancel();
    _videoPositionSub?.cancel();
    _videoPlayingSub?.cancel();

    _audioPlayer.dispose();
    _videoController?.dispose();

    super.dispose();
  }
}

Log

tter (12085): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
   I/flutter (12085): │ 🐛 Updated playedAt for /storage/emulated/0/Download/Seal/TOAST/#ババババンビ「ゲイシャフジヤマ」Official Music Video .mkv
   I/flutter (12085): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): │ #0 PlaybackProvider.playMedia (package:beplayed/providers/playback_provider.dart:515:12)
   I/flutter (12085): │ #1 MediaProvider.initiatePlayback (package:beplayed/providers/media_provider.dart:202:31)
   I/flutter (12085): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
   I/flutter (12085): │ 💡 PlaybackProvider: Preparing #ババババンビ「ゲイシャフジヤマ」Official Music Video .mkv, filePath: /storage/emulated/0/Download/Seal/TOAST/#ババババンビ「ゲイシャフジヤマ」Official Music Video .mkv, autoPlay: true, media.duration: 230295
   I/flutter (12085): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): │ #0 SubtitleController._startPositionListening (package:beplayed/widgets/player/subtitle_controller.dart:98:14)
   I/flutter (12085): │ #1 new SubtitleController (package:beplayed/widgets/player/subtitle_controller.dart:74:5)
   I/flutter (12085): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
   I/flutter (12085): │ 💡 SubtitleController: Attached to PlaybackProvider.positionListenable
   I/flutter (12085): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): │ #0 SubtitleController.loadEmbeddedSubtitles (package:beplayed/widgets/player/subtitle_controller.dart:238:14)
   I/flutter (12085): │ #1 PlaybackProvider.playMedia (package:beplayed/providers/playback_provider.dart:524:30)
   I/flutter (12085): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
   I/flutter (12085): │ 💡 SubtitleController: Media changed to #ババババンビ「ゲイシャフジヤマ」Official Music Video, resetting state
   I/flutter (12085): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): │ #0 SubtitleController.autoApplySubtitleRules (package:beplayed/widgets/player/subtitle_controller.dart:885:14)
   I/flutter (12085): │ #1    I/flutter (12085): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
   I/flutter (12085): │ 💡 Global subtitles OFF → disabling
   I/flutter (12085): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): │ #0 SubtitleController.loadAudioTracks (package:beplayed/widgets/player/subtitle_controller.dart:318:16)
   I/flutter (12085): │ #1 PlaybackProvider.playMedia (package:beplayed/providers/playback_provider.dart:525:30)
   I/flutter (12085): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
   I/flutter (12085): │ 💡 SubtitleController: Defaulted to audio track: 0
   I/flutter (12085): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): │ #0 MediaProvider.getLastPosition (package:beplayed/providers/media_provider.dart:842:16)
   I/flutter (12085): │ #1    I/flutter (12085): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
   I/flutter (12085): │ ! No playback position found for /storage/emulated/0/Download/Seal/TOAST/#ババババンビ「ゲイシャフジヤマ」Official Music Video .mkv
   I/flutter (12085): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   W/libOpenSLES(12085): Leaving Object::GetInterface (SL_RESULT_FEATURE_UNSUPPORTED)
   W/libOpenSLES(12085): Leaving Object::GetInterface (SL_RESULT_FEATURE_UNSUPPORTED)
   I/flutter (12085): fvp.FINE: 14:42:37.648: 1049571674 player-5476376630545911296 create(file:///storage/emulated/0/Download/Seal/TOAST/#ババババンビ「ゲイシャフジヤマ」Official Music Video .mkv)
   I/flutter (12085): mdk.FINE: 14:42:37.667: default 0xb400007838217400 new FrameReader...
   I/flutter (12085): mdk.FINE: 14:42:37.667: Registered audio backends: OpenSL AudioTrack null
   I/flutter (12085): mdk.FINE: 14:42:37.667: OpenSL extensions: ANDROID_SDK_LEVEL_34
   I/flutter (12085): mdk.FINE: 14:42:37.667: default 0xb400007838214c00 new FrameReader...
   I/flutter (12085): mdk.FINE: 14:42:37.667: Registered audio backends: OpenSL AudioTrack null
   I/flutter (12085): mdk.FINE: 14:42:37.668: OpenSL extensions: ANDROID_SDK_LEVEL_34
   I/flutter (12085): mdk.FINE: 14:42:37.668: 0xb4000078914e3300 player.Player()
   I/flutter (12085): mdk.FINE: 14:42:37.668: 0xb4000078914e3300 player.onEvent(1, 0x7fd6198780)
   I/flutter (12085): mdk.FINE: 14:42:37.668: 0xb4000078914e3300 player.onStateChanged(1)
   I/flutter (12085): mdk.FINE: 14:42:37.668: 0xb4000078914e3300 player.onMediaStatus(1)
   I/flutter (12085): mdk.FINE: 14:42:37.668: 0xb4000078914e3300 player property: video.decoder = shader_resource=0
   I/flutter (12085): mdk.FINE: 14:42:37.668: 0xb4000078914e3300 player property: avformat.strict = experimental
   I/flutter (12085): mdk.FINE: 14:42:37.669: 0xb4000078914e3300 player property: avformat.safe = 0
   I/flutter (12085): mdk.FINE: 14:42:37.669: 0xb4000078914e3300 player property: avio.reconnect = 1
   I/flutter (12085): mdk.FINE: 14:42:37.669: 0xb4000078914e3300 player property: avio.reconnect_delay_max = 7
   I/flutter (12085): mdk.FINE: 14:42:37.669: 0xb4000078914e3300 player property: avformat.rtsp_transport = tcp
   I/flutter (12085): mdk.FINE: 14:42:37.669: 0xb4000078914e3300 player property: avformat.extension_picky = 0
   I/flutter (12085): mdk.FINE: 14:42:37.669: 0xb4000078914e3300 player property: avformat.allowed_segment_extensions = ALL
   I/flutter (12085): mdk.FINE: 14:42:37.669: 0xb4000078914e3300 player property: avio.protocol_whitelist = file,ftp,rtmp,http,https,tls,rtp,tcp,udp,crypto,httpproxy,data,concatf,concat,subfile
   I/flutter (12085): mdk.FINE: 14:42:37.670: 0xb4000078914e3300 player.setDecoders(video, [D3D11VA, NVDEC, VAAPI, VideoToolbox, AMediaCodec, FFmpeg])
   I/flutter (12085): mdk.FINE: 14:42:37.670: mdk plugin 'mdk-d3d11va': 0x0 from dir:
   I/flutter (12085): mdk.FINE: 14:42:37.670: mdk plugin 'mdk-nvdec': 0x0 from dir:
   I/flutter (12085): mdk.FINE: 14:42:37.670: mdk plugin 'mdk-videotoolbox': 0x0 from dir:
   I/flutter (12085): mdk.FINE: 14:42:37.670: video decoders: "D3D11VA", "NVDEC", "VAAPI", "VideoToolbox", "AMediaCodec", "FFmpeg"
   I/flutter (12085): mdk.FINE: 14:42:37.670: video decoders: "D3D11VA", "NVDEC", "VAAPI", "VideoToolbox", "AMediaCodec", "FFmpeg"
   I/flutter (12085): mdk.FINE: 14:42:37.670: 0xb4000078914e3300 player property: avformat.fflags = +nobuffer
   I/flutter (12085): mdk.FINE: 14:42:37.670: 0xb4000078914e3300 player property: avformat.fpsprobesize = 0
   I/flutter (12085): mdk.FINE: 14:42:37.671: 0xb4000078914e3300 player property: avformat.analyzeduration = 100000
   I/flutter (12085): mdk.FINE: 14:42:37.671: 0xb4000078914e3300 player.setBufferRange(0, -1, 0)
   I/flutter (12085): mdk.FINE: 14:42:37.671: 0xb4000078914e3300 player.setMedia(file:///storage/emulated/0/Download/Seal/TOAST/#ババババンビ「ゲイシャフジヤマ」Official Music Video .mkv)
   I/flutter (12085): mdk.FINE: 14:42:37.671: default FrameReader0xb400007838217400 state: 0=>0=>0, 0
   I/flutter (12085): mdk.INFO: 14:42:37.672: default0xb400007838217400 stop, current state: , drequested: 0
   I/flutter (12085): mdk.FINE: 14:42:37.673: default FrameReader0xb400007838217400 state: 0=>0=>0, 0
   I/flutter (12085): mdk.INFO: 14:42:37.674: default0xb400007838217400 stop, current state: , drequested: 0
   I/flutter (12085): mdk.FINE: 14:42:37.674: 0xb4000078914e3300 player.set(0)
   I/flutter (12085): mdk.FINE: 14:42:37.674: 0xb40000789194d800 virtual void mdk::MediaControlPush::setState(PlaybackState)@1125 requested state 0=>0, current state 0. status: 0
   I/flutter (12085): mdk.FINE: 14:42:37.674: default FrameReader0xb400007838217400 state: 0=>0=>0, 0
   I/flutter (12085): mdk.INFO: 14:42:37.674: default0xb400007838217400 stop, current state: , drequested: 0
   I/flutter (12085): mdk.FINE: 14:42:37.675: 0xb4000078914e3300 player.waitFor(0, -1)
   I/flutter (12085): mdk.FINE: 14:42:37.675: 0xb4000078914e3300 player.prepare(0, ..., 1282)
   I/flutter (12085): mdk.INFO: 14:42:37.675: 0xb40000789194d800 MediaControl.prepare(0, ...) file:///storage/emulated/0/Download/Seal/TOAST/#ババババンビ 「ゲイシャフジヤマ」Official Music Video .mkv
   I/flutter (12085): mdk.FINE: 14:42:37.675: default 0xb400007838217400 FrameReader.start(0, ...)
   I/flutter (12085): mdk.FINE: 14:42:37.675: default 0xb400007838217400 FrameReader::update MediaStatus 0=>0X2
   I/flutter (12085): fvp.FINE: 14:42:37.678: 673301634 player-5476376630545911296 onMediaStatus: MediaStatus(noMedia) => MediaStatus(+loading)
   I/flutter (12085): mdk.FINE: 14:42:37.680: default FrameReader0xb400007838217400 state: 0=>0=>1, 0
   I/flutter (12085): mdk.FINE: 14:42:37.680: default FrameReader0xb400007838217400 state: 0=>1=>1, 0
   I/flutter (12085): mdk.FINE: 14:42:37.680: default FrameReader0xb400007838217400 request to pause 1, loaded: 0.
   I/flutter (12085): mdk.FINE: 14:42:37.681: default FrameReader0xb400007838217400 state: 0=>1=>2, 0
   I/flutter (12085): mdk.FINE: 14:42:37.681: default FrameReader0xb400007838217400 state requested: 2, current: 0
   I/flutter (12085): mdk.FINE: 14:42:37.681: 0xb400007838217400start frame reader thread: 516780678320
   I/flutter (12085): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): │ #0 AndroidPlatformAdapter.getStorageDirectory (package:beplayed/services/platform_adapter.dart:85:14)
   I/flutter (12085): │ #1    I/flutter (12085): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
   I/flutter (12085): │ 💡 [AndroidPlatformAdapter] Storage directory: /storage/emulated/0/Android/data/com.beplayed/files
   I/flutter (12085): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   I/flutter (12085): mdk.FINE: 14:42:37.682: FFmpeg/Libav runtime git-2025-11-15-e096a59-avbuild
   I/flutter (12085): mdk.FINE: 14:42:37.682: Selected avutil runtime version: 60.17.100 (build: 60.17.100), license: LGPL version 2.1 or later
   I/flutter (12085): mdk.FINE: 14:42:37.682: global option: ffmpeg.configuration = --extra-version=avbuild --disable-doc --disable-debug --disable-static --enable-shared --enable-runtime-cpudetect --disable-iamf --enable-mediacodec --enable-jni --disable-vulkan --disable-v4l2-m2m --disable-response-files --disable-indevs --enable-indev=android_camera --disable-outdevs --ar=llvm-ar --ranlib=llvm-ranlib --nm=llvm-nm --strip=llvm-strip --target-os=android --arch=arm64 --enable-cross-compile --cross-prefix=aarch64-linux-android- --pkg-config=pkg-config --cc=clang --extra-ldexeflags='-Wl,--gc-sections -Wl,-z,nocopyreloc -pie -fPIE ' --enable-lto --enable-pic --extra-cflags='-Wa,--noexecstack -fdata-sections -ffunction-sections -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIE -ffast-math -fstrict-aliasing --target=aarch64-none-linux-android21 -D__BIONIC_NO_PAGE_SIZE_MACRO=1' --extra-cxxflags='-Wa,--noexecstack -fdata-sections -ffunction-sections -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIE -
   I/flutter (12085): mdk.FINE: 14:42:37.682: av_dict_set0x785bf89aa4
   D/permissions_handler(12085): No permissions found in manifest for: []15
   D/BufferQueueConsumer(12085): connect: controlledByApp=true
   I/flutter (12085): mdk.FINE: 14:42:37.685: Selected avformat runtime version: 62.6.103 (build: 62.6.103), license: LGPL version 2.1 or later
   D/BufferQueueConsumer(12085): ImageReader-16x16f22u256m5-12085-0 disconnect
   D/BufferQueueConsumer(12085): connect: controlledByApp=true
   I/flutter (12085): mdk.FINE: 14:42:37.686: avformat_version0x785bec88a4
   F/libc (12085): Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8 in tid 12746 (Thread-11), pid 12085 (com.beplayed)
   *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
   Build fingerprint: 'Redmi/rubypro_in/rubypro:14/UP1A.231005.007/OS2.0.7.0.UMOINXM:user/release-keys'
   Revision: '0'
   ABI: 'arm64'
   Timestamp: 2025-12-09 14:42:37.893108958+0530
   Process uptime: 31s
   Cmdline: com.beplayed
   pid: 12085, tid: 12746, name: Thread-11 >>> com.beplayed <<<
   uid: 10011
   signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000008
   Cause: null pointer dereference
       x0 0000000000000000 x1 000000785e83665a x2 0000000000000000 x3 0000000000000000
       x4 0000000000000000 x5 0006110f00000000 x6 0000000000000002 x7 0032314433440075
       x8 000000000000002c x9 00000000000000a4 x10 0000000000000002 x11 0101010101010101
       x12 0000000000000067 x13 0000000000000003 x14 0000000000000000 x15 0000000000000080
       x16 000000785ab8d038 x17 00000079bb7633a0 x18 00000076fd258000 x19 0000007832177330
       x20 b4000078368aaf60 x21 0000000000000001 x22 0000000000003a1a x23 0000000000000003
   lags='-Wa,--noexecstack -fdata-sections -ffunction-sections -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIE -
   I/flutter (12085): mdk.FINE: 14:42:37.682: av_dict_set0x785bf89aa4
   D/permissions_handler(12085): No permissions found in manifest for: []15
   D/BufferQueueConsumer(12085): connect: controlledByApp=true
   I/flutter (12085): mdk.FINE: 14:42:37.685: Selected avformat runtime version: 62.6.103 (build: 62.6.103), license: LGPL version 2.1 or later
   D/BufferQueueConsumer(12085): ImageReader-16x16f22u256m5-12085-0 disconnect
   D/BufferQueueConsumer(12085): connect: controlledByApp=true
   I/flutter (12085): mdk.FINE: 14:42:37.686: avformat_version0x785bec88a4
   F/libc (12085): Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8 in tid 12746 (Thread-11), pid 12085 (com.beplayed)
   *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
   Build fingerprint: 'Redmi/rubypro_in/rubypro:14/UP1A.231005.007/OS2.0.7.0.UMOINXM:user/release-keys'
   Revision: '0'
   ABI: 'arm64'
   Timestamp: 2025-12-09 14:42:37.893108958+0530
   Process uptime: 31s
   Cmdline: com.beplayed
   pid: 12085, tid: 12746, name: Thread-11 >>> com.beplayed <<<
   uid: 10011
   signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000008
   Cause: null pointer dereference
       x0 0000000000000000 x1 000000785e83665a x2 0000000000000000 x3 0000000000000000
       x4 0000000000000000 x5 0006110f00000000 x6 0000000000000002 x7 0032314433440075
       x8 000000000000002c x9 00000000000000a4 x10 0000000000000002 x11 0101010101010101
       x12 0000000000000067 x13 0000000000000003 x14 0000000000000000 x15 0000000000000080
       x16 000000785ab8d038 x17 00000079bb7633a0 x18 00000076fd258000 x19 0000007832177330
       x20 b4000078368aaf60 x21 0000000000000001 x22 0000000000003a1a x23 0000000000000003
   D/BufferQueueConsumer(12085): connect: controlledByApp=true
   I/flutter (12085): mdk.FINE: 14:42:37.685: Selected avformat runtime version: 62.6.103 (build: 62.6.103), license: LGPL version 2.1 or later
   D/BufferQueueConsumer(12085): ImageReader-16x16f22u256m5-12085-0 disconnect
   D/BufferQueueConsumer(12085): connect: controlledByApp=true
   I/flutter (12085): mdk.FINE: 14:42:37.686: avformat_version0x785bec88a4
   F/libc (12085): Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8 in tid 12746 (Thread-11), pid 12085 (com.beplayed)
   *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
   Build fingerprint: 'Redmi/rubypro_in/rubypro:14/UP1A.231005.007/OS2.0.7.0.UMOINXM:user/release-keys'
   Revision: '0'
   ABI: 'arm64'
   Timestamp: 2025-12-09 14:42:37.893108958+0530
   Process uptime: 31s
   Cmdline: com.beplayed
   pid: 12085, tid: 12746, name: Thread-11 >>> com.beplayed <<<
   uid: 10011
   signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000008
   Cause: null pointer dereference
       x0 0000000000000000 x1 000000785e83665a x2 0000000000000000 x3 0000000000000000
       x4 0000000000000000 x5 0006110f00000000 x6 0000000000000002 x7 0032314433440075
       x8 000000000000002c x9 00000000000000a4 x10 0000000000000002 x11 0101010101010101
       x12 0000000000000067 x13 0000000000000003 x14 0000000000000000 x15 0000000000000080
       x16 000000785ab8d038 x17 00000079bb7633a0 x18 00000076fd258000 x19 0000007832177330
       x20 b4000078368aaf60 x21 0000000000000001 x22 0000000000003a1a x23 0000000000000003
   Build fingerprint: 'Redmi/rubypro_in/rubypro:14/UP1A.231005.007/OS2.0.7.0.UMOINXM:user/release-keys'
   Revision: '0'
   ABI: 'arm64'
   Timestamp: 2025-12-09 14:42:37.893108958+0530
   Process uptime: 31s
   Cmdline: com.beplayed
   pid: 12085, tid: 12746, name: Thread-11 >>> com.beplayed <<<
   uid: 10011
   signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000008
   Cause: null pointer dereference
       x0 0000000000000000 x1 000000785e83665a x2 0000000000000000 x3 0000000000000000
       x4 0000000000000000 x5 0006110f00000000 x6 0000000000000002 x7 0032314433440075
       x8 000000000000002c x9 00000000000000a4 x10 0000000000000002 x11 0101010101010101
       x12 0000000000000067 x13 0000000000000003 x14 0000000000000000 x15 0000000000000080
       x16 000000785ab8d038 x17 00000079bb7633a0 x18 00000076fd258000 x19 0000007832177330
       x20 b4000078368aaf60 x21 0000000000000001 x22 0000000000003a1a x23 0000000000000003
   signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000008
   Cause: null pointer dereference
       x0 0000000000000000 x1 000000785e83665a x2 0000000000000000 x3 0000000000000000
       x4 0000000000000000 x5 0006110f00000000 x6 0000000000000002 x7 0032314433440075
       x8 000000000000002c x9 00000000000000a4 x10 0000000000000002 x11 0101010101010101
       x12 0000000000000067 x13 0000000000000003 x14 0000000000000000 x15 0000000000000080
       x16 000000785ab8d038 x17 00000079bb7633a0 x18 00000076fd258000 x19 0000007832177330
       x20 b4000078368aaf60 x21 0000000000000001 x22 0000000000003a1a x23 0000000000000003
       x16 000000785ab8d038 x17 00000079bb7633a0 x18 00000076fd258000 x19 0000007832177330
       x20 b4000078368aaf60 x21 0000000000000001 x22 0000000000003a1a x23 0000000000000003
       x20 b4000078368aaf60 x21 0000000000000001 x22 0000000000003a1a x23 0000000000000003
       x24 b400007836720880 x25 000000785e9e3000 x26 b400007826321080 x27 000000785e9e36d0
       x28 0000007832178000 x29 0000007832177350
       lr 000000785e999c4c sp 00000078321772b0 pc 000000785e999c5c pst 0000000080001000
   6 total frames
   backtrace:
         #00 pc 0000000000199c5c /data/app/~~pd4texBj44w4g9HzVvma9A==/com.beplayed-qMJfF_Eblyfh6vsRh1bFOw==/base.apk!libmdk.so (offset 0x5dd8000) (BuildId: 155563568f67b35cbbbb487ee5664ab7e1c8cf82)
         #1 pc 000000000019cdbc /data/app/~~pd4texBj44w4g9HzVvma9A==/com.beplayed-qMJfF_Eblyfh6vsRh1bFOw==/base.apk!libmdk.so (offset 0x5dd8000) (BuildId: 155563568f67b35cbbbb487ee5664ab7e1c8cf82)
         #2 pc 00000000000cdfc0 /data/app/~~pd4texBj44w4g9HzVvma9A==/com.beplayed-qMJfF_Eblyfh6vsRh1bFOw==/base.apk!libmdk.so (offset 0x5dd8000) (BuildId: 155563568f67b35cbbbb487ee5664ab7e1c8cf82)
         #3 pc 00000000000caaf0 /data/app/~~pd4texBj44w4g9HzVvma9A==/com.beplayed-qMJfF_Eblyfh6vsRh1bFOw==/base.apk!libmdk.so (offset 0x5dd8000) (BuildId: 155563568f67b35cbbbb487ee5664ab7e1c8cf82)
         #4 pc 0000000000101d5c /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+204) (BuildId: 84a42637b3a421b801818f5793418fca)
         #5 pc 0000000000095bc0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 84a42637b3a421b801818f5793418fca)
   Lost connection to device.
   PS C:\Users\syuvr\Desktop\beplayed>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions