File: RTCAudioDevice_xctest.mm

package info (click to toggle)
firefox-esr 140.5.0esr-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,538,920 kB
  • sloc: cpp: 7,381,527; javascript: 6,388,905; ansic: 3,710,087; python: 1,393,776; xml: 628,165; asm: 426,916; java: 184,004; sh: 65,744; makefile: 19,302; objc: 13,059; perl: 12,912; yacc: 4,583; cs: 3,846; pascal: 3,352; lex: 1,720; ruby: 1,226; exp: 762; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10
file content (180 lines) | stat: -rw-r--r-- 6,874 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
 *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#import <XCTest/XCTest.h>

#include <stdlib.h>

#include "api/task_queue/default_task_queue_factory.h"

#import "sdk/objc/components/audio/RTCAudioSession+Private.h"
#import "sdk/objc/native/api/audio_device_module.h"
#import "sdk/objc/native/src/audio/audio_device_ios.h"

@interface RTCAudioDeviceTests : XCTestCase {
  bool _testEnabled;
  rtc::scoped_refptr<webrtc::AudioDeviceModule> _audioDeviceModule;
  std::unique_ptr<webrtc::ios_adm::AudioDeviceIOS> _audio_device;
}

@property(nonatomic) RTC_OBJC_TYPE(RTCAudioSession) * audioSession;

@end

@implementation RTCAudioDeviceTests

@synthesize audioSession = _audioSession;

- (void)setUp {
  [super setUp];
#if defined(WEBRTC_IOS) && TARGET_OS_SIMULATOR
  // TODO(peterhanspers): Reenable these tests on simulator.
  // See bugs.webrtc.org/7812
  _testEnabled = false;
  if (::getenv("WEBRTC_IOS_RUN_AUDIO_TESTS") != nullptr) {
    _testEnabled = true;
  }
#else
  _testEnabled = true;
#endif

  _audioDeviceModule = webrtc::CreateAudioDeviceModule();
  _audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(
      /*bypass_voice_processing=*/false,
      /*muted_speech_event_handler=*/nullptr,
      /*render_error_handler=*/nullptr));
  self.audioSession = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];

  NSError *error = nil;
  [self.audioSession lockForConfiguration];
  [self.audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
                     withOptions:0
                           error:&error];
  XCTAssertNil(error);

  [self.audioSession setMode:AVAudioSessionModeVoiceChat error:&error];
  XCTAssertNil(error);

  [self.audioSession setActive:YES error:&error];
  XCTAssertNil(error);

  [self.audioSession unlockForConfiguration];
}

- (void)tearDown {
  _audio_device->Terminate();
  _audio_device.reset(nullptr);
  _audioDeviceModule = nullptr;
  [self.audioSession notifyDidEndInterruptionWithShouldResumeSession:NO];

  [super tearDown];
}

// Verifies that the AudioDeviceIOS is_interrupted_ flag is reset correctly
// after an iOS AVAudioSessionInterruptionTypeEnded notification event.
// AudioDeviceIOS listens to RTC_OBJC_TYPE(RTCAudioSession) interrupted
// notifications by:
// - In AudioDeviceIOS.InitPlayOrRecord registers its audio_session_observer_
//   callback with RTC_OBJC_TYPE(RTCAudioSession)'s delegate list.
// - When RTC_OBJC_TYPE(RTCAudioSession) receives an iOS audio interrupted
// notification, it
//   passes the notification to callbacks in its delegate list which sets
//   AudioDeviceIOS's is_interrupted_ flag to true.
// - When AudioDeviceIOS.ShutdownPlayOrRecord is called, its
//   audio_session_observer_ callback is removed from RTCAudioSessions's
//   delegate list.
//   So if RTC_OBJC_TYPE(RTCAudioSession) receives an iOS end audio interruption
//   notification, AudioDeviceIOS is not notified as its callback is not in
//   RTC_OBJC_TYPE(RTCAudioSession)'s delegate list. This causes
//   AudioDeviceIOS's is_interrupted_ flag to be in the wrong (true) state and
//   the audio session will ignore audio changes.
// As RTC_OBJC_TYPE(RTCAudioSession) keeps its own interrupted state, the fix is
// to initialize AudioDeviceIOS's is_interrupted_ flag to
// RTC_OBJC_TYPE(RTCAudioSession)'s isInterrupted flag in
// AudioDeviceIOS.InitPlayOrRecord.
- (void)testInterruptedAudioSession {
  XCTSkipIf(!_testEnabled);
  XCTAssertTrue(self.audioSession.isActive);
  XCTAssertTrue(
      [self.audioSession.category
          isEqual:AVAudioSessionCategoryPlayAndRecord] ||
      [self.audioSession.category isEqual:AVAudioSessionCategoryPlayback]);
  XCTAssertEqual(AVAudioSessionModeVoiceChat, self.audioSession.mode);

  std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
      webrtc::CreateDefaultTaskQueueFactory();
  std::unique_ptr<webrtc::AudioDeviceBuffer> audio_buffer;
  audio_buffer.reset(new webrtc::AudioDeviceBuffer(task_queue_factory.get()));
  _audio_device->AttachAudioBuffer(audio_buffer.get());
  XCTAssertEqual(webrtc::AudioDeviceGeneric::InitStatus::OK,
                 _audio_device->Init());
  XCTAssertEqual(0, _audio_device->InitPlayout());
  XCTAssertEqual(0, _audio_device->StartPlayout());

  // Force interruption.
  [self.audioSession notifyDidBeginInterruption];

  // Wait for notification to propagate.
  rtc::ThreadManager::ProcessAllMessageQueuesForTesting();
  XCTAssertTrue(_audio_device->IsInterrupted());

  // Force it for testing.
  _audio_device->StopPlayout();

  [self.audioSession notifyDidEndInterruptionWithShouldResumeSession:YES];
  // Wait for notification to propagate.
  rtc::ThreadManager::ProcessAllMessageQueuesForTesting();
  XCTAssertTrue(_audio_device->IsInterrupted());

  _audio_device->Init();
  _audio_device->InitPlayout();
  XCTAssertFalse(_audio_device->IsInterrupted());
}

- (void)testMuteSpeechHandlerCalledWithStartedWhenSpeechActivityHasStarted {
  XCTestExpectation *handlerExpectation =
      [self expectationWithDescription:@"mutedSpeechHandler"];
  webrtc::AudioDeviceModule::MutedSpeechEventHandler
      muted_speech_event_handler = ^void(
          webrtc::AudioDeviceModule::MutedSpeechEvent event) {
        XCTAssertEqual(event, webrtc::AudioDeviceModule::kMutedSpeechStarted);
        [handlerExpectation fulfill];
      };

  _audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(
      /*bypass_voice_processing=*/false,
      /*muted_speech_event_handler=*/muted_speech_event_handler,
      /*render_error_handler=*/nullptr));

  _audio_device->OnReceivedMutedSpeechActivity(
      kAUVoiceIOSpeechActivityHasStarted);
  [self waitForExpectations:@[ handlerExpectation ] timeout:10.0];
}

- (void)testMuteSpeechHandlerCalledWithEndedWhenSpeechActivityHasEnded {
  XCTestExpectation *handlerExpectation =
      [self expectationWithDescription:@"mutedSpeechHandler"];
  webrtc::AudioDeviceModule::MutedSpeechEventHandler
      muted_speech_event_handler =
          ^void(webrtc::AudioDeviceModule::MutedSpeechEvent event) {
            XCTAssertEqual(event, webrtc::AudioDeviceModule::kMutedSpeechEnded);
            [handlerExpectation fulfill];
          };

  _audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(
      /*bypass_voice_processing=*/false,
      /*muted_speech_event_handler=*/muted_speech_event_handler,
      /*render_error_handler=*/nullptr));
  _audio_device->OnReceivedMutedSpeechActivity(
      kAUVoiceIOSpeechActivityHasEnded);
  [self waitForExpectations:@[ handlerExpectation ] timeout:10.0];
}

@end