Skip to content
This repository was archived by the owner on Mar 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions packages/audiofileplayer/ios/Classes/AudiofileplayerPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
static NSString *const kReleaseMethod = @"release";
static NSString *const kPlayMethod = @"play";
static NSString *const kPlayFromStart = @"playFromStart";
static NSString *const kEndpointSeconds = @"endpointSeconds";
static NSString *const kSeekMethod = @"seek";
static NSString *const kSetVolumeMethod = @"setVolume";
static NSString *const kVolume = @"volume";
Expand Down Expand Up @@ -80,16 +81,21 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result

if ([call.method isEqualToString:kPlayMethod]) {
bool playFromStart = [call.arguments[kPlayFromStart] boolValue];
[player play:playFromStart];
NSNumber *endpointSecondsNumber = call.arguments[kEndpointSeconds];
NSTimeInterval endpoint =
endpointSecondsNumber ? [endpointSecondsNumber doubleValue] : FLTManagedPlayerPlayToEnd;
[player play:playFromStart endpoint:endpoint];
result(nil);
} else if ([call.method isEqualToString:kReleaseMethod]) {
[player releasePlayer];
[_playersDict removeObjectForKey:audioId];
result(nil);
} else if ([call.method isEqualToString:kSeekMethod]) {
NSTimeInterval position = [call.arguments[kPositionSeconds] doubleValue];
[player seek:position];
result(nil);
[player seek:position
completionHandler:^() {
result(nil);
}];
} else if ([call.method isEqualToString:kSetVolumeMethod]) {
double volume = [call.arguments[kVolume] doubleValue];
[player setVolume:volume];
Expand Down
13 changes: 10 additions & 3 deletions packages/audiofileplayer/ios/Classes/ManagedPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

@protocol FLTManagedPlayerDelegate

extern NSTimeInterval const FLTManagedPlayerPlayToEnd;

/**
* Called by FLTManagedPlayer when a non-looping sound has finished playback,
* or on calling stop().
Expand Down Expand Up @@ -40,10 +42,15 @@
delegate:(id<FLTManagedPlayerDelegate>)delegate
isLooping:(bool)isLooping
remoteLoadHandler:(void (^)(BOOL))remoteLoadHandler;

- (void)play:(bool)playFromStart;
/**
* Plays the audio data.
*
* @param endpoint the time, as an NSTimeInterval, to play to. To play until
* the end, pass FLTManagedPlayerPlayToEnd.
*/
- (void)play:(bool)playFromStart endpoint:(NSTimeInterval)endpoint;
- (void)releasePlayer;
- (void)seek:(NSTimeInterval)position;
- (void)seek:(NSTimeInterval)position completionHandler:(void (^)())completionHandler;
- (void)setVolume:(double)volume;
- (void)pause;

Expand Down
81 changes: 69 additions & 12 deletions packages/audiofileplayer/ios/Classes/ManagedPlayer.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#import <AVFoundation/AVFoundation.h>

NSTimeInterval const FLTManagedPlayerPlayToEnd = -1.0;

static NSString *const kKeyPathStatus = @"status";
static float const kTimerUpdateIntervalSeconds = 0.25;

Expand All @@ -17,6 +19,7 @@ @implementation FLTManagedPlayer {
id _completionObserver; // Registered on NSNotificationCenter.
id _timeObserver; // Registered on the AVPlayer.
void (^_remoteLoadHandler)(BOOL); // Called on AVPlayer loading status change observed.
NSTimer *_endpointTimer;
}

// Private common initializer. [audioPlayer] or [avPlayer], but not both, must be set.
Expand All @@ -41,21 +44,25 @@ - (instancetype)initWithAudioId:(NSString *)audioId
_audioPlayer.numberOfLoops = isLooping ? -1 : 0;
[_audioPlayer prepareToPlay];
[_delegate managedPlayerDidLoadWithDuration:_audioPlayer.duration forAudioId:_audioId];
__weak FLTManagedPlayer *weakSelf = self;
_positionTimer = [NSTimer
scheduledTimerWithTimeInterval:kTimerUpdateIntervalSeconds
repeats:YES
block:^(NSTimer *timer) {
if (_audioPlayer.playing) {
[_delegate
managedPlayerDidUpdatePosition:_audioPlayer.currentTime
forAudioId:_audioId];
FLTManagedPlayer *strongSelf = weakSelf;
if (strongSelf) {
if (strongSelf->_audioPlayer.playing) {
[strongSelf->_delegate
managedPlayerDidUpdatePosition:_audioPlayer.currentTime
forAudioId:strongSelf->_audioId];
}
}
}];
} else {
_avPlayer = avPlayer;
_remoteLoadHandler = remoteLoadHandler;
CMTime interval = CMTimeMakeWithSeconds(kTimerUpdateIntervalSeconds, NSEC_PER_SEC);
FLTManagedPlayer *__weak weakSelf = self;
__weak FLTManagedPlayer *weakSelf = self;
_timeObserver = [_avPlayer
addPeriodicTimeObserverForInterval:interval
queue:nil
Expand All @@ -74,8 +81,11 @@ - (instancetype)initWithAudioId:(NSString *)audioId
object:_avPlayer.currentItem
queue:nil
usingBlock:^(NSNotification *notif) {
[_avPlayer seekToTime:kCMTimeZero];
[_delegate managedPlayerDidFinishPlaying:_audioId];
FLTManagedPlayer *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf->_avPlayer seekToTime:kCMTimeZero];
[strongSelf->_delegate managedPlayerDidFinishPlaying:_audioId];
}
}];
[_avPlayer.currentItem addObserver:self
forKeyPath:kKeyPathStatus
Expand Down Expand Up @@ -134,17 +144,60 @@ - (void)dealloc {
[_avPlayer removeTimeObserver:_timeObserver];
}

- (void)play:(bool)playFromStart {
- (void)play:(bool)playFromStart endpoint:(NSTimeInterval)endpoint {
// Maybe seek to start.
if (_audioPlayer) {
if (playFromStart) {
_audioPlayer.currentTime = 0;
}
[_audioPlayer play];
} else {
if (playFromStart) {
[_avPlayer seekToTime:kCMTimeZero];
}
[_avPlayer play];
}
// Handle endpoint timers and start playback.
if (endpoint == FLTManagedPlayerPlayToEnd) {
// No endpoint, clear timer and start playback.
[_endpointTimer invalidate];
_endpointTimer = nil;
if (_audioPlayer) {
[_audioPlayer play];
} else {
[_avPlayer play];
}
} else {
// If there is an endpoint, check that it is in the future, then start playback and schedule
// the pausing after a duration.
NSTimeInterval position;
if (_audioPlayer) {
position = _audioPlayer.currentTime;
} else {
position = (NSTimeInterval)CMTimeGetSeconds(_avPlayer.currentTime);
}
NSTimeInterval duration = endpoint - position;
NSLog(@"Called play() at position %.2f seconds, to play for duration %.2f seconds.", position,
duration);
if (duration <= 0) {
NSLog(@"Called play() at position after endpoint. No playback occurred.");
return;
}
[_endpointTimer invalidate];
if (_audioPlayer) {
[_audioPlayer play];
} else {
[_avPlayer play];
}
__weak FLTManagedPlayer *weakSelf = self;
_endpointTimer = [NSTimer
scheduledTimerWithTimeInterval:duration
repeats:NO
block:^(NSTimer *timer) {
FLTManagedPlayer *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf pause];
[strongSelf->_delegate managedPlayerDidFinishPlaying:_audioId];
}
}];
}
}

Expand All @@ -161,11 +214,15 @@ - (void)releasePlayer {
}
}

- (void)seek:(NSTimeInterval)position {
- (void)seek:(NSTimeInterval)position completionHandler:(void (^)())completionHandler {
if (_audioPlayer) {
_audioPlayer.currentTime = position;
completionHandler();
} else {
[_avPlayer seekToTime:CMTimeMakeWithSeconds(position, NSEC_PER_SEC)];
[_avPlayer seekToTime:CMTimeMakeWithSeconds(position, NSEC_PER_SEC)
completionHandler:^(BOOL completed) {
completionHandler();
}];
}
}

Expand Down