Skip to content

audioOutputDevice remains unchanged on iOS #1106

@qu0cquyen

Description

@qu0cquyen

Using triggeriOSAudioRouteSelectionUI from RtcMediaDeviceNotifier.instance seems to be unable to update audioOutputDevice from call.state.valueOrNull.audioOutputDevice

This is how we trigger audio output changes:

/// Toggle Outputs devices.
  /// I.e: Switch outputs (listenable) to Speaker, headphones, earpieces, etc...
  ///
  /// @params: [Call]
  FutureOr<void> onToggleSpeaker(Call call) async {
    // state = call;
    final callState = call.state.valueOrNull;

    if (callState == null) {
      return;
    }

    // If current device is IOS, we want to delegate to system auto audio routing
    if (CurrentPlatform.isIos) {
      await _deviceNotifier.triggeriOSAudioRouteSelectionUI();

      return;
    }

    // If current device is Android, we want to display a list of availabes
    // audio outputs and let user choose from them.
    await _showAvaialbeOutputsDevices(
      call: call,
      onDevicePressed: (output, input) async {
        await call.setAudioOutputDevice(output);

        await call.setAudioInputDevice(input);
      },
    );
}

This is the log that we try to implement a native call to get current selected audio outputs
Image

Code Example:

Future<void> _observeiOSAudioChanges(MethodCall method) async {
    if (method.method == 'onAudioRouteChange') {
      final Map<dynamic, dynamic> deviceInfo =
          method.arguments as Map<dynamic, dynamic>;

      final String label = deviceInfo['label'] as String;
      final String deviceId = deviceInfo['deviceId'] as String;
      final String kindAlias = deviceInfo['kind'] as String;

      // Use the provided factory method for safe enum conversion
      final RtcMediaDeviceKind kind = RtcMediaDeviceKind.fromAlias(kindAlias);

      // Map the incoming data to an RtcMediaDevice object
      final RtcMediaDevice newDevice = RtcMediaDevice(
        label: label,
        id: deviceId,
        kind: kind,
      );

      dev.log(
          "Audio route changed to RtcMediaDevice: ${newDevice.toString()})");

      // Now you can use this `newDevice` object to update your CallState or RtcMediaDeviceNotifier
      // Example (adjust based on your actual state management):
      // _deviceNotifier.setActiveOutputDevice(newDevice);
      // if (state != null) {
      //   // Access internal logic of the SDK to update the audio state if possible
      // }
    } else {
      throw PlatformException(
          code: 'Unrecognized method',
          message: 'Method ${method.method} not recognized');
    }
  }

This is the log from the UI, when we try to retrieve current audioOutputDevice from the callState

Image
return BetterStreamBuilder(
        stream: call.state.valueStream,
        builder: (context, callState) {
          log('AUDIO OUTPUT UIUI: ${callState.audioOutputDevice}');

          return StreamCallContent(
            call: call,
            pictureInPictureConfiguration: const PictureInPictureConfiguration(
              enablePictureInPicture: false,
              disablePictureInPictureWhenScreenSharing: true,
            ),
            callAppBarWidgetBuilder: (context, call) {
              final isTeam = callState.callType.value == CallType.team.type;
              CustomLog.info('CASADAS: ${callState.custom}');
              final groupTeam =
                  (callState.custom['channel_name'] as String? ?? '').isEmpty
                      ? callState.custom['callee_name'] as String? ?? ''
                      : callState.custom['channel_name'] as String? ?? '';
              return CallAppBar(
                call: call,
                elevation: 0,
                showLeaveCallAction: true,
                backgroundColor: AppColor.darkPrimaryColor,
                title: const SizedBox.shrink(),
                leadingWidth: MediaQuery.sizeOf(context).width * 0.8,
                leading: Row(
                  children: [
                    IconButton(
                      icon: const Icon(
                        Icons.arrow_back_ios_new_rounded,
                        color: Colors.white,
                      ),
                      onPressed: () => GoRouter.of(context).pop(),
                    ),
                    if (isTeam)
                      Expanded(
                        child: Text(
                          groupTeam,
                          overflow: TextOverflow.ellipsis,
                          maxLines: 1,
                          style: const TextStyle(
                            color: Colors.white,
                            fontWeight: FontWeight.w500,
                          ),
                        ),
                      ),
                  ],
                ),
                actions: [
                  Padding(
                    padding: const EdgeInsets.only(right: 8.0),
                    child: Consumer(
                      builder: (context, ref, child) {
                        final isLocalCameraOn =
                            callState.localParticipant?.isVideoEnabled ?? false;

                        if (!isLocalCameraOn) {
                          return const SizedBox.shrink();
                        }

                        return InkWell(
                          onTap: () async => await ref
                              .read(incompanyCallProvider.notifier)
                              .onFlipCamera(call),
                          child: SvgPicture.asset(
                            'assets/icons/camera-switch.svg',
                            colorFilter: const ColorFilter.mode(
                              Colors.white,
                              BlendMode.srcIn,
                            ),
                          ),
                        );
                      },
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.only(right: 8.0),
                    child: Badge.count(
                      count: callState.participantCount,
                      isLabelVisible: isTeam,
                      child: SvgPicture.asset(
                        call.type.value == CallType.inCompany.type
                            ? 'assets/icons/user-round.svg'
                            : 'assets/icons/users-round.svg',
                        colorFilter: const ColorFilter.mode(
                          Colors.white,
                          BlendMode.srcIn,
                        ),
                      ),
                    ),
                  ),
                ],
              );
            },
            callParticipantsWidgetBuilder: (context, call) {
              return CallInCompanyParticipants(
                call: call,
                callState: callState,
              );
            },
            callControlsWidgetBuilder: (context, call) {
              final cs = call.state.valueOrNull;
              log('AUDIO OUTPUT Call COntrols: ${cs?.audioOutputDevice}');

              return DecoratedBox(
                decoration: BoxDecoration(color: AppColor.darkPrimaryColor),
                child: Padding(
                  padding:
                      const EdgeInsets.only(bottom: kBottomNavigationBarHeight),
                  child: CallInCompanyCallControls(
                    call: call,
                    callState: callState,
                  ),
                ),
              );
            },
          );
        });

SDK's version:
stream_video: ^0.11.2
stream_video_flutter: ^0.11.2
stream_video_push_notification: ^0.11.2
stream_video_screen_sharing: ^0.11.2

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions