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:
tkchin 2016-03-12 16:52:04 -08:00 committed by Commit bot
parent b25345ee3f
commit 0ce3bf9cc4
12 changed files with 122 additions and 20 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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];
}

View File

@ -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

View File

@ -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;

View File

@ -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();
}
}

View 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];
}

View File

@ -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': [

View File

@ -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',