Fix lock behavior on RTCAudioSession.
In addition: - Introduces RTCAudioSessionTest - iOS/Mac gtests now have an autoreleasepool - Moves ScopedAutoreleasePool to rtc_base_approved - Introduces route change button in AppRTCDemo BUG=webrtc:5649 Review URL: https://codereview.webrtc.org/1782363002 Cr-Commit-Position: refs/heads/master@{#11971}
This commit is contained in:
parent
b25345ee3f
commit
0ce3bf9cc4
@ -16,6 +16,8 @@ typedef NS_ENUM(NSInteger, RTCDispatcherQueueType) {
|
||||
// Used for starting/stopping AVCaptureSession, and assigning
|
||||
// capture session to AVCaptureVideoPreviewLayer.
|
||||
RTCDispatcherTypeCaptureSession,
|
||||
// Used for operations on AVAudioSession.
|
||||
RTCDispatcherTypeAudioSession,
|
||||
};
|
||||
|
||||
/** Dispatcher that asynchronously dispatches blocks to a specific
|
||||
|
||||
@ -10,15 +10,17 @@
|
||||
|
||||
#import "RTCDispatcher.h"
|
||||
|
||||
static dispatch_queue_t kAudioSessionQueue = nil;
|
||||
static dispatch_queue_t kCaptureSessionQueue = nil;
|
||||
|
||||
@implementation RTCDispatcher {
|
||||
dispatch_queue_t _captureSessionQueue;
|
||||
}
|
||||
@implementation RTCDispatcher
|
||||
|
||||
+ (void)initialize {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
kAudioSessionQueue = dispatch_queue_create(
|
||||
"org.webrtc.RTCDispatcherAudioSession",
|
||||
DISPATCH_QUEUE_SERIAL);
|
||||
kCaptureSessionQueue = dispatch_queue_create(
|
||||
"org.webrtc.RTCDispatcherCaptureSession",
|
||||
DISPATCH_QUEUE_SERIAL);
|
||||
@ -39,6 +41,8 @@ static dispatch_queue_t kCaptureSessionQueue = nil;
|
||||
return dispatch_get_main_queue();
|
||||
case RTCDispatcherTypeCaptureSession:
|
||||
return kCaptureSessionQueue;
|
||||
case RTCDispatcherTypeAudioSession:
|
||||
return kAudioSessionQueue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,9 @@
|
||||
// Called when the camera switch button is pressed.
|
||||
- (void)videoCallViewDidSwitchCamera:(ARDVideoCallView *)view;
|
||||
|
||||
// Called when the route change button is pressed.
|
||||
- (void)videoCallViewDidChangeRoute:(ARDVideoCallView *)view;
|
||||
|
||||
// Called when the hangup button is pressed.
|
||||
- (void)videoCallViewDidHangup:(ARDVideoCallView *)view;
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ static CGFloat const kStatusBarHeight = 20;
|
||||
@end
|
||||
|
||||
@implementation ARDVideoCallView {
|
||||
UIButton *_routeChangeButton;
|
||||
UIButton *_cameraSwitchButton;
|
||||
UIButton *_hangupButton;
|
||||
CGSize _remoteVideoSize;
|
||||
@ -48,12 +49,23 @@ static CGFloat const kStatusBarHeight = 20;
|
||||
_statsView.hidden = YES;
|
||||
[self addSubview:_statsView];
|
||||
|
||||
_routeChangeButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_routeChangeButton.backgroundColor = [UIColor whiteColor];
|
||||
_routeChangeButton.layer.cornerRadius = kButtonSize / 2;
|
||||
_routeChangeButton.layer.masksToBounds = YES;
|
||||
UIImage *image = [UIImage imageNamed:@"ic_surround_sound_black_24dp.png"];
|
||||
[_routeChangeButton setImage:image forState:UIControlStateNormal];
|
||||
[_routeChangeButton addTarget:self
|
||||
action:@selector(onRouteChange:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
[self addSubview:_routeChangeButton];
|
||||
|
||||
// TODO(tkchin): don't display this if we can't actually do camera switch.
|
||||
_cameraSwitchButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_cameraSwitchButton.backgroundColor = [UIColor whiteColor];
|
||||
_cameraSwitchButton.layer.cornerRadius = kButtonSize / 2;
|
||||
_cameraSwitchButton.layer.masksToBounds = YES;
|
||||
UIImage *image = [UIImage imageNamed:@"ic_switch_video_black_24dp.png"];
|
||||
image = [UIImage imageNamed:@"ic_switch_video_black_24dp.png"];
|
||||
[_cameraSwitchButton setImage:image forState:UIControlStateNormal];
|
||||
[_cameraSwitchButton addTarget:self
|
||||
action:@selector(onCameraSwitch:)
|
||||
@ -140,6 +152,12 @@ static CGFloat const kStatusBarHeight = 20;
|
||||
CGRectGetMaxX(cameraSwitchFrame) + kButtonPadding;
|
||||
_cameraSwitchButton.frame = cameraSwitchFrame;
|
||||
|
||||
// Place route button to the right of camera button.
|
||||
CGRect routeChangeFrame = _cameraSwitchButton.frame;
|
||||
routeChangeFrame.origin.x =
|
||||
CGRectGetMaxX(routeChangeFrame) + kButtonPadding;
|
||||
_routeChangeButton.frame = routeChangeFrame;
|
||||
|
||||
[_statusLabel sizeToFit];
|
||||
_statusLabel.center =
|
||||
CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
@ -160,6 +178,10 @@ static CGFloat const kStatusBarHeight = 20;
|
||||
[_delegate videoCallViewDidSwitchCamera:self];
|
||||
}
|
||||
|
||||
- (void)onRouteChange:(id)sender {
|
||||
[_delegate videoCallViewDidChangeRoute:self];
|
||||
}
|
||||
|
||||
- (void)onHangup:(id)sender {
|
||||
[_delegate videoCallViewDidHangup:self];
|
||||
}
|
||||
|
||||
@ -10,6 +10,9 @@
|
||||
|
||||
#import "ARDVideoCallViewController.h"
|
||||
|
||||
#import "webrtc/base/objc/RTCDispatcher.h"
|
||||
#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h"
|
||||
|
||||
#import "RTCAVFoundationVideoSource.h"
|
||||
#import "RTCLogging.h"
|
||||
|
||||
@ -27,6 +30,7 @@
|
||||
ARDAppClient *_client;
|
||||
RTCVideoTrack *_remoteVideoTrack;
|
||||
RTCVideoTrack *_localVideoTrack;
|
||||
AVAudioSessionPortOverride _portOverride;
|
||||
}
|
||||
|
||||
@synthesize videoCallView = _videoCallView;
|
||||
@ -117,6 +121,26 @@
|
||||
[self switchCamera];
|
||||
}
|
||||
|
||||
- (void)videoCallViewDidChangeRoute:(ARDVideoCallView *)view {
|
||||
AVAudioSessionPortOverride override = AVAudioSessionPortOverrideNone;
|
||||
if (_portOverride == AVAudioSessionPortOverrideNone) {
|
||||
override = AVAudioSessionPortOverrideSpeaker;
|
||||
}
|
||||
[RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeAudioSession
|
||||
block:^{
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
[session lockForConfiguration];
|
||||
NSError *error = nil;
|
||||
if ([session overrideOutputAudioPort:override error:&error]) {
|
||||
_portOverride = override;
|
||||
} else {
|
||||
RTCLogError(@"Error overriding output port: %@",
|
||||
error.localizedDescription);
|
||||
}
|
||||
[session unlockForConfiguration];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)videoCallViewDidEnableStats:(ARDVideoCallView *)view {
|
||||
_client.shouldGetStats = YES;
|
||||
_videoCallView.statsView.hidden = NO;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 285 B |
Binary file not shown.
|
After Width: | Height: | Size: 570 B |
@ -14,9 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RTCAudioSession ()
|
||||
|
||||
/** The lock that guards access to AVAudioSession methods. */
|
||||
@property(nonatomic, strong) NSRecursiveLock *lock;
|
||||
|
||||
/** The delegates. */
|
||||
@property(nonatomic, readonly) NSSet *delegates;
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h"
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/criticalsection.h"
|
||||
|
||||
#import "webrtc/base/objc/RTCLogging.h"
|
||||
#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h"
|
||||
@ -22,15 +23,15 @@ NSInteger const kRTCAudioSessionErrorLockRequired = -1;
|
||||
// TODO(tkchin): Consider more granular locking. We're not expecting a lot of
|
||||
// lock contention so coarse locks should be fine for now.
|
||||
@implementation RTCAudioSession {
|
||||
rtc::CriticalSection _crit;
|
||||
AVAudioSession *_session;
|
||||
NSHashTable *_delegates;
|
||||
NSInteger _activationCount;
|
||||
NSInteger _lockRecursionCount;
|
||||
BOOL _isActive;
|
||||
BOOL _isLocked;
|
||||
}
|
||||
|
||||
@synthesize session = _session;
|
||||
@synthesize lock = _lock;
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
@ -45,7 +46,7 @@ NSInteger const kRTCAudioSessionErrorLockRequired = -1;
|
||||
if (self = [super init]) {
|
||||
_session = [AVAudioSession sharedInstance];
|
||||
_delegates = [NSHashTable weakObjectsHashTable];
|
||||
_lock = [[NSRecursiveLock alloc] init];
|
||||
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
[center addObserver:self
|
||||
selector:@selector(handleInterruptionNotification:)
|
||||
@ -86,7 +87,7 @@ NSInteger const kRTCAudioSessionErrorLockRequired = -1;
|
||||
|
||||
- (BOOL)isLocked {
|
||||
@synchronized(self) {
|
||||
return _isLocked;
|
||||
return _lockRecursionCount > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,24 +104,23 @@ NSInteger const kRTCAudioSessionErrorLockRequired = -1;
|
||||
}
|
||||
|
||||
- (void)lockForConfiguration {
|
||||
[_lock lock];
|
||||
_crit.Enter();
|
||||
@synchronized(self) {
|
||||
_isLocked = YES;
|
||||
++_lockRecursionCount;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)unlockForConfiguration {
|
||||
// Don't let threads other than the one that called lockForConfiguration
|
||||
// unlock.
|
||||
if ([_lock tryLock]) {
|
||||
if (_crit.TryEnter()) {
|
||||
@synchronized(self) {
|
||||
_isLocked = NO;
|
||||
--_lockRecursionCount;
|
||||
}
|
||||
// One unlock for the tryLock, and another one to actually unlock. If this
|
||||
// was called without anyone calling lock, the underlying NSRecursiveLock
|
||||
// should spit out an error.
|
||||
[_lock unlock];
|
||||
[_lock unlock];
|
||||
// was called without anyone calling lock, we will hit an assertion.
|
||||
_crit.Leave();
|
||||
_crit.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
44
webrtc/modules/audio_device/ios/objc/RTCAudioSessionTest.mm
Normal file
44
webrtc/modules/audio_device/ios/objc/RTCAudioSessionTest.mm
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2016 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 <Foundation/Foundation.h>
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h"
|
||||
|
||||
@interface RTCAudioSessionTest : NSObject
|
||||
|
||||
- (void)testLockForConfiguration;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RTCAudioSessionTest
|
||||
|
||||
- (void)testLockForConfiguration {
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
[session lockForConfiguration];
|
||||
EXPECT_TRUE(session.isLocked);
|
||||
}
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
EXPECT_TRUE(session.isLocked);
|
||||
[session unlockForConfiguration];
|
||||
}
|
||||
EXPECT_FALSE(session.isLocked);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
TEST(RTCAudioSessionTest, LockForConfiguration) {
|
||||
RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
|
||||
[test testLockForConfiguration];
|
||||
}
|
||||
@ -472,9 +472,13 @@
|
||||
],
|
||||
}],
|
||||
['OS=="ios"', {
|
||||
'includes': [
|
||||
'../build/objc_common.gypi',
|
||||
],
|
||||
'sources': [
|
||||
'video_coding/codecs/h264/h264_video_toolbox_nalu_unittest.cc',
|
||||
'audio_device/ios/audio_device_unittest_ios.cc',
|
||||
'audio_device/ios/objc/RTCAudioSessionTest.mm',
|
||||
],
|
||||
# This needs to be kept in sync with modules_unittests.isolate.
|
||||
'mac_bundle_resources': [
|
||||
|
||||
@ -281,14 +281,16 @@
|
||||
'conditions': [
|
||||
['OS=="ios"', {
|
||||
'mac_bundle_resources': [
|
||||
'examples/objc/AppRTCDemo/ios/resources/Roboto-Regular.ttf',
|
||||
'examples/objc/AppRTCDemo/ios/resources/iPhone5@2x.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/iPhone6@2x.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/iPhone6p@3x.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/Roboto-Regular.ttf',
|
||||
'examples/objc/AppRTCDemo/ios/resources/ic_call_end_black_24dp.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/ic_call_end_black_24dp@2x.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/ic_clear_black_24dp.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/ic_clear_black_24dp@2x.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/ic_surround_sound_black_24dp.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/ic_surround_sound_black_24dp@2x.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/ic_switch_video_black_24dp.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/ic_switch_video_black_24dp@2x.png',
|
||||
'examples/objc/AppRTCDemo/ios/resources/mozart.mp3',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user