-
Notifications
You must be signed in to change notification settings - Fork 3.3k
[video_player_avfoundation, camera_avfoundation] never overwrite but only upgrade audio session category #7143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
787f41c
ef43db2
94a80df
f637396
86922ac
6746391
42bd00a
fde86f6
2f876dd
4ddc291
81a9449
4dd42c1
e02355c
6569354
1086400
9edbeef
6216653
59a7fab
8f013b6
acaad4c
3d4e638
948937e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -237,6 +237,8 @@ - (instancetype)initWithMediaSettings:(FCPPlatformMediaSettings *)mediaSettings | |
_videoFormat = kCVPixelFormatType_32BGRA; | ||
_inProgressSavePhotoDelegates = [NSMutableDictionary dictionary]; | ||
_fileFormat = FCPPlatformImageFileFormatJpeg; | ||
_videoCaptureSession.automaticallyConfiguresApplicationAudioSession = NO; | ||
_audioCaptureSession.automaticallyConfiguresApplicationAudioSession = NO; | ||
|
||
// To limit memory consumption, limit the number of frames pending processing. | ||
// After some testing, 4 was determined to be the best maximum value. | ||
|
@@ -725,7 +727,8 @@ - (void)captureOutput:(AVCaptureOutput *)output | |
if (_isFirstVideoSample) { | ||
[_videoWriter startSessionAtSourceTime:currentSampleTime]; | ||
// fix sample times not being numeric when pause/resume happens before first sample buffer | ||
// arrives https://github.com/flutter/flutter/issues/132014 | ||
// arrives | ||
// https://github.com/flutter/flutter/issues/132014 | ||
_lastVideoSampleTime = currentSampleTime; | ||
_lastAudioSampleTime = currentSampleTime; | ||
_isFirstVideoSample = NO; | ||
|
@@ -1283,9 +1286,7 @@ - (BOOL)setupWriterForPath:(NSString *)path { | |
return NO; | ||
} | ||
|
||
if (_mediaSettings.enableAudio && !_isAudioSetup) { | ||
[self setUpCaptureSessionForAudio]; | ||
} | ||
[self setUpCaptureSessionForAudioIfNeeded]; | ||
|
||
_videoWriter = [[AVAssetWriter alloc] initWithURL:outputURL | ||
fileType:AVFileTypeMPEG4 | ||
|
@@ -1365,9 +1366,42 @@ - (BOOL)setupWriterForPath:(NSString *)path { | |
return YES; | ||
} | ||
|
||
- (void)setUpCaptureSessionForAudio { | ||
// This function, although slightly modified, is also in video_player_avfoundation. | ||
// Both need to do the same thing and run on the same thread (for example main thread). | ||
// Configure application wide audio session manually to prevent overwriting flag | ||
// MixWithOthers by capture session. | ||
// Only change category if it is considered an upgrade which means it can only enable | ||
// ability to play in silent mode or ability to record audio but never disables it, | ||
// that could affect other plugins which depend on this global state. Only change | ||
// category or options if there is change to prevent unnecessary lags and silence. | ||
static void upgradeAudioSessionCategory(AVAudioSessionCategory requestedCategory, | ||
AVAudioSessionCategoryOptions options) { | ||
NSSet *playCategories = [NSSet | ||
setWithObjects:AVAudioSessionCategoryPlayback, AVAudioSessionCategoryPlayAndRecord, nil]; | ||
NSSet *recordCategories = | ||
[NSSet setWithObjects:AVAudioSessionCategoryRecord, AVAudioSessionCategoryPlayAndRecord, nil]; | ||
NSSet *requiredCategories = | ||
[NSSet setWithObjects:requestedCategory, AVAudioSession.sharedInstance.category, nil]; | ||
BOOL requiresPlay = [requiredCategories intersectsSet:playCategories]; | ||
BOOL requiresRecord = [requiredCategories intersectsSet:recordCategories]; | ||
if (requiresPlay && requiresRecord) { | ||
requestedCategory = AVAudioSessionCategoryPlayAndRecord; | ||
} else if (requiresPlay) { | ||
requestedCategory = AVAudioSessionCategoryPlayback; | ||
} else if (requiresRecord) { | ||
requestedCategory = AVAudioSessionCategoryRecord; | ||
} | ||
options = AVAudioSession.sharedInstance.categoryOptions | options; | ||
if ([requestedCategory isEqualToString:AVAudioSession.sharedInstance.category] && | ||
options == AVAudioSession.sharedInstance.categoryOptions) { | ||
return; | ||
} | ||
[AVAudioSession.sharedInstance setCategory:requestedCategory withOptions:options error:nil]; | ||
} | ||
|
||
- (void)setUpCaptureSessionForAudioIfNeeded { | ||
// Don't setup audio twice or we will lose the audio. | ||
if (_isAudioSetup) { | ||
if (!_mediaSettings.enableAudio || _isAudioSetup) { | ||
return; | ||
} | ||
|
||
|
@@ -1383,6 +1417,20 @@ - (void)setUpCaptureSessionForAudio { | |
// Setup the audio output. | ||
_audioOutput = [[AVCaptureAudioDataOutput alloc] init]; | ||
|
||
dispatch_block_t block = ^{ | ||
// Set up options implicit to AVAudioSessionCategoryPlayback to avoid conflicts with other | ||
// plugins like video_player. | ||
upgradeAudioSessionCategory(AVAudioSessionCategoryPlayAndRecord, | ||
AVAudioSessionCategoryOptionDefaultToSpeaker | | ||
AVAudioSessionCategoryOptionAllowBluetoothA2DP | | ||
AVAudioSessionCategoryOptionAllowAirPlay); | ||
}; | ||
if (!NSThread.isMainThread) { | ||
dispatch_sync(dispatch_get_main_queue(), block); | ||
} else { | ||
block(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is called on the main thread in tests if I remember correctly #7143 (comment). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it only called on main thread in tests? Can you give an example of such failure? I'd like to see if it's possible to run on background thread in those tests There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try to run tests with this: //if (!NSThread.isMainThread) {
dispatch_sync(dispatch_get_main_queue(), block);
//} else {
//block();
//} And you will get this inside xcode:
When running outside of xcode with debugging there will be probably just crash, you can add You will probably need to run
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Could you explain the problem with FLTEnsureToRunOnMainQueue? Maybe a concrete example?
I don't know why you are comparing with interfaces and factories. Imagine you have this code:
Here your production code only calls Logic A, and test code only calls Logic B. Then the test isn't really providing any confidence right? What I suggest is:
Then in the test, you can do:
This way, the test code actually covers Logic A, providing us with confidence in production. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your assertion will fail because other tests call
What I proposed does not have such problems and tests also cover production logic as I wrote. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh I overlooked the part that it's
It would be the same problem, that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh I did not think of that, but as you wrote that, I realized that means that also the async test should be untestable, maybe it actually waits in some non blocking way. But anyway I did not see that documented anywhere, maybe that async test is not 100% clean then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
if ([_audioCaptureSession canAddInput:audioInput]) { | ||
[_audioCaptureSession addInput:audioInput]; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -113,7 +113,7 @@ NS_ASSUME_NONNULL_BEGIN | |
- (void)startImageStreamWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger; | ||
- (void)stopImageStream; | ||
- (void)setZoomLevel:(CGFloat)zoom withCompletion:(void (^)(FlutterError *_Nullable))completion; | ||
- (void)setUpCaptureSessionForAudio; | ||
- (void)setUpCaptureSessionForAudioIfNeeded; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this called outside the implementation? If not it should be moved to the private category in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is also called from |
||
|
||
@end | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.