record method

Future<MiniProgramMicrophoneResult> record({
  1. required String miniProgramId,
  2. required MiniProgramMicrophonePolicy policy,
  3. int maxDurationSeconds = 60,
  4. VoidCallback? onStarted,
})

Implementation

Future<MiniProgramMicrophoneResult> record({
  required String miniProgramId,
  required MiniProgramMicrophonePolicy policy,
  int maxDurationSeconds = 60,
  VoidCallback? onStarted,
}) async {
  if (_disposed) throw StateError('Microphone manager is disposed.');
  if (!policy.enabled) {
    throw const MiniProgramMicrophoneException(
      errorCode: MiniProgramErrorCodes.microphoneNotAccepted,
      message: 'Microphone recording is not accepted by host policy.',
    );
  }
  if (maxDurationSeconds < 1 ||
      maxDurationSeconds > 600 ||
      policy.maxDurationSeconds < 1 ||
      policy.maxDurationSeconds > 600 ||
      policy.maxBytes < 1 ||
      policy.maxBytes > 100 * 1024 * 1024) {
    throw const MiniProgramMicrophoneException(
      errorCode: MiniProgramErrorCodes.microphoneInvalidRequest,
      message: 'The recording request or accepted limits are invalid.',
    );
  }
  if (_active != null) {
    throw const MiniProgramMicrophoneException(
      errorCode: MiniProgramErrorCodes.microphoneRequestInProgress,
      message: 'A microphone recording is already in progress.',
    );
  }
  final operation = _MicrophoneOperation(
    'recording_${DateTime.now().microsecondsSinceEpoch}_${++_sequence}',
    miniProgramId,
  );
  _active = operation;
  final duration = math.min(maxDurationSeconds, policy.maxDurationSeconds);
  MiniProgramMicrophoneResult? result;
  bool registered = false;
  Object? callbackError;
  try {
    result = await provider.record(
      MiniProgramMicrophoneRequest(
        recordingId: operation.id,
        miniProgramId: miniProgramId,
        maxDurationSeconds: duration,
        maxBytes: policy.maxBytes,
      ),
      onStarted: () {
        if (_disposed ||
            operation.cancelled ||
            !identical(_active, operation) ||
            operation.started) {
          return;
        }
        operation.started = true;
        try {
          onStarted?.call();
        } on Object catch (error) {
          callbackError = error;
          unawaited(cancelFor(miniProgramId));
        }
      },
    );
    if (operation.cancelled || _disposed) {
      throw const MiniProgramMicrophoneException(
        errorCode: MiniProgramErrorCodes.microphoneRecordingCancelled,
        message: 'Microphone recording was cancelled.',
      );
    }
    result.validate();
    if (result.recordingId != operation.id ||
        mediaManager.isRegistered(result.mediaRef)) {
      throw const MiniProgramMicrophoneException(
        errorCode: MiniProgramErrorCodes.microphoneInvalidResult,
        message: 'The host returned an invalid recording reference.',
      );
    }
    // Native encoders may finalize a frame just beyond the timer boundary.
    if (result.bytes > policy.maxBytes ||
        result.durationMs > duration * 1000 + 1000) {
      throw const MiniProgramMicrophoneException(
        errorCode: MiniProgramErrorCodes.microphoneLimitExceeded,
        message: 'The recording exceeds accepted host limits.',
      );
    }
    mediaManager.register(
      miniProgramId: miniProgramId,
      mediaRef: result.mediaRef,
    );
    registered = true;
    _owners[result.mediaRef] = miniProgramId;
    return result;
  } catch (error) {
    if (result != null) {
      if (registered) {
        await releaseRecording(miniProgramId, result);
      } else if (!mediaManager.isRegistered(result.mediaRef)) {
        await _releaseNative(miniProgramId, result.mediaRef);
      }
    }
    if (callbackError != null) throw callbackError!;
    if (error is FormatException || error is MiniProgramMediaException) {
      throw const MiniProgramMicrophoneException(
        errorCode: MiniProgramErrorCodes.microphoneInvalidResult,
        message: 'The host returned invalid recording metadata.',
      );
    }
    rethrow;
  } finally {
    if (identical(_active, operation)) _active = null;
  }
}