You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
366 lines
10 KiB
366 lines
10 KiB
#include "PlayerFFmpeg.h"
|
|
|
|
@implementation PlayerFFmpeg
|
|
|
|
@synthesize delegate = _delegate;
|
|
|
|
- (void)dealloc
|
|
{
|
|
[self stop];
|
|
}
|
|
|
|
- (id)init
|
|
{
|
|
self = [super init];
|
|
return self;
|
|
}
|
|
|
|
- (void)setupPlayer:(NSArray *)options
|
|
{
|
|
[FFMoviePlayerController setLogReport:NO];
|
|
[FFMoviePlayerController setLogLevel:k_LOG_SILENT];
|
|
[FFMoviePlayerController checkIfFFmpegVersionMatch:NO];
|
|
|
|
_ffOptions = [FFOptions optionsByDefault];
|
|
[_ffOptions setPlayerOptionIntValue:1 forKey:@"start-on-prepared"];
|
|
|
|
for(NSString *option in options)
|
|
{
|
|
NSArray *optionData = [option componentsSeparatedByString:@"="];
|
|
if ([optionData count] > 1)
|
|
{
|
|
[_ffOptions setPlayerOptionIntValue:[optionData[1] integerValue] forKey:optionData[0]];
|
|
}
|
|
else
|
|
{
|
|
if ([optionData[0] isEqual: @"play-in-background"])
|
|
{
|
|
_playInBackground = true;
|
|
continue;
|
|
}
|
|
|
|
if ([optionData[0] isEqual: @"rtsp-tcp"])
|
|
{
|
|
[_ffOptions setFormatOptionValue:@"tcp" forKey:@"rtsp_transport"];
|
|
continue;
|
|
}
|
|
|
|
[_ffOptions setPlayerOptionIntValue:1 forKey:optionData[0]];
|
|
}
|
|
}
|
|
|
|
[self initMediaPlayer];
|
|
}
|
|
|
|
- (void)initMediaPlayer
|
|
{
|
|
_player = [[FFMoviePlayerController alloc] initWithOptions:_ffOptions];
|
|
[_player setPauseInBackground:!_playInBackground];
|
|
_cachedPlaybackTime = -1;
|
|
_cachedBuffering = -1;
|
|
_hasVideoTrack = false;
|
|
_isBuffering = false;
|
|
|
|
[self installMovieNotificationObservers];
|
|
}
|
|
|
|
- (void)setDataSource:(NSString *)path
|
|
{
|
|
_videoPath = path;
|
|
}
|
|
|
|
- (void)play
|
|
{
|
|
if (_player == nil)
|
|
[self initMediaPlayer];
|
|
|
|
if (![_player isPreparedToPlay])
|
|
{
|
|
[_player setDataSourceURL:[NSURL URLWithString:_videoPath]];
|
|
[_player prepareToPlay];
|
|
|
|
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
PlayerState *newState = [[PlayerState alloc] init];
|
|
newState.state = Opening;
|
|
[_delegate mediaPlayerStateChanged:newState];
|
|
});
|
|
}
|
|
}
|
|
else
|
|
[_player play];
|
|
}
|
|
|
|
- (void)pause
|
|
{
|
|
[_player pause];
|
|
}
|
|
|
|
- (void)stop
|
|
{
|
|
if (_player != nil)
|
|
{
|
|
[_player shutdown];
|
|
[self removeMovieNotificationObservers];
|
|
_player = nil;
|
|
|
|
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
|
|
{
|
|
PlayerState *newState = [[PlayerState alloc] init];
|
|
newState.state = Stopped;
|
|
[_delegate mediaPlayerStateChanged:newState];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (int)getDuration
|
|
{
|
|
return [_player duration] * 1000;
|
|
}
|
|
|
|
- (CVPixelBufferRef)getPixelBuffer
|
|
{
|
|
return [_player videoBuffer];
|
|
}
|
|
|
|
- (int)getFramesCounter
|
|
{
|
|
if (_isBuffering && _cachedBuffering != _player.bufferingProgress
|
|
&& _cachedBuffering < _player.bufferingProgress)
|
|
{
|
|
_cachedBuffering = _player.bufferingProgress;
|
|
|
|
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
PlayerState *newState = [[PlayerState alloc] init];
|
|
newState.state = Buffering;
|
|
newState.valueFloat = (float)_cachedBuffering;
|
|
[_delegate mediaPlayerStateChanged:newState];
|
|
});
|
|
}
|
|
}
|
|
|
|
if ([self isReady] && [self isPlaying])
|
|
{
|
|
float currentTime = [_player currentPlaybackTime];
|
|
if (fabs(currentTime - _cachedPlaybackTime) > TIME_CHANGE_OFFSET)
|
|
{
|
|
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
PlayerState *newState = [[PlayerState alloc] init];
|
|
newState.state = TimeChanged;
|
|
newState.valueLong = currentTime * 1000;
|
|
[_delegate mediaPlayerStateChanged:newState];
|
|
});
|
|
}
|
|
|
|
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
PlayerState *newState = [[PlayerState alloc] init];
|
|
newState.state = PositionChanged;
|
|
newState.valueFloat = currentTime / [_player duration];
|
|
[_delegate mediaPlayerStateChanged:newState];
|
|
});
|
|
}
|
|
|
|
_cachedPlaybackTime = [_player currentPlaybackTime];
|
|
}
|
|
}
|
|
|
|
return (int)[_player frameCount];
|
|
}
|
|
|
|
|
|
- (int)getVolume
|
|
{
|
|
return [_player playbackVolume] * 100;
|
|
}
|
|
|
|
- (void)setVolume:(int)value
|
|
{
|
|
[_player setPlaybackVolume:(float)value / 100.0];
|
|
}
|
|
|
|
- (int)getTime
|
|
{
|
|
return [_player currentPlaybackTime] * 1000;
|
|
}
|
|
|
|
- (void)setTime:(int)value
|
|
{
|
|
[_player setCurrentPlaybackTime:value];
|
|
}
|
|
|
|
- (float)getPosition
|
|
{
|
|
float position = [_player currentPlaybackTime] / [_player duration];
|
|
return position;
|
|
}
|
|
|
|
- (void)setPosition:(float)value
|
|
{
|
|
float position = [_player duration] * value;
|
|
[_player setCurrentPlaybackTime:position];
|
|
}
|
|
|
|
- (float)getPlaybackRate
|
|
{
|
|
return [_player playbackRate];
|
|
}
|
|
|
|
- (void)setPlaybackRate:(float)value
|
|
{
|
|
[_player setPlaybackRate:value];
|
|
}
|
|
|
|
- (bool)isPlaying
|
|
{
|
|
return [_player isPlaying];
|
|
}
|
|
|
|
- (bool)isReady
|
|
{
|
|
return _hasVideoTrack ? [_player frameCount] > 0 : [_player isPreparedToPlay];
|
|
}
|
|
|
|
- (int)getVideoWidth
|
|
{
|
|
return [_player videoWidth];
|
|
}
|
|
|
|
- (int)getVideoHeight
|
|
{
|
|
return [_player videoHeight];
|
|
}
|
|
|
|
- (void)movieVideoDecoderOpen:(NSNotification*)notification
|
|
{
|
|
_hasVideoTrack = true;
|
|
}
|
|
|
|
- (void)loadStateDidChange:(NSNotification*)notification
|
|
{
|
|
switch (_player.loadState)
|
|
{
|
|
case MMPMovieLoadStateStalled:
|
|
_isBuffering = true;
|
|
break;
|
|
|
|
case MMPMovieLoadStatePlayable | MMPMovieLoadStatePlaythroughOK:
|
|
_isBuffering = false;
|
|
_cachedBuffering = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (void)moviePlayBackDidFinish:(NSNotification*)notification
|
|
{
|
|
int reason = [[[notification userInfo] valueForKey:MMPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
|
|
|
|
switch (reason)
|
|
{
|
|
case MMPMovieFinishReasonPlaybackEnded:
|
|
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
PlayerState *newState = [[PlayerState alloc] init];
|
|
newState.state = EndReached;
|
|
[_delegate mediaPlayerStateChanged:newState];
|
|
});
|
|
}
|
|
break;
|
|
|
|
case MMPMovieFinishReasonUserExited:
|
|
break;
|
|
|
|
case MMPMovieFinishReasonPlaybackError:
|
|
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
PlayerState *newState = [[PlayerState alloc] init];
|
|
newState.state = EncounteredError;
|
|
[_delegate mediaPlayerStateChanged:newState];
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (void)mediaIsPreparedToPlayDidChange:(NSNotification*)notification
|
|
{
|
|
NSLog(@"mediaIsPreparedToPlayDidChange\n");
|
|
}
|
|
|
|
- (void)moviePlayBackStateDidChange:(NSNotification*)notification
|
|
{
|
|
switch (_player.playbackState)
|
|
{
|
|
case MMPMoviePlaybackStatePlaying:
|
|
break;
|
|
|
|
case MMPMoviePlaybackStatePaused:
|
|
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
PlayerState *newState = [[PlayerState alloc] init];
|
|
newState.state = Paused;
|
|
[_delegate mediaPlayerStateChanged:newState];
|
|
});
|
|
}
|
|
break;
|
|
|
|
case MMPMoviePlaybackStateInterrupted:
|
|
break;
|
|
|
|
case MMPMoviePlaybackStateSeekingForward:
|
|
case MMPMoviePlaybackStateSeekingBackward:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
-(void)installMovieNotificationObservers
|
|
{
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(loadStateDidChange:)
|
|
name:MMPMoviePlayerLoadStateDidChangeNotification
|
|
object:_player];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(moviePlayBackDidFinish:)
|
|
name:MMPMoviePlayerPlaybackDidFinishNotification
|
|
object:_player];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(mediaIsPreparedToPlayDidChange:)
|
|
name:MMPMediaPlaybackIsPreparedToPlayDidChangeNotification
|
|
object:_player];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(moviePlayBackStateDidChange:)
|
|
name:MMPMoviePlayerPlaybackStateDidChangeNotification
|
|
object:_player];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(movieVideoDecoderOpen:)
|
|
name:MMPMoviePlayerVideoDecoderOpenNotification
|
|
object:_player];
|
|
}
|
|
|
|
-(void)removeMovieNotificationObservers
|
|
{
|
|
[[NSNotificationCenter defaultCenter]removeObserver:self name:MMPMoviePlayerLoadStateDidChangeNotification object:_player];
|
|
[[NSNotificationCenter defaultCenter]removeObserver:self name:MMPMoviePlayerPlaybackDidFinishNotification object:_player];
|
|
[[NSNotificationCenter defaultCenter]removeObserver:self name:MMPMediaPlaybackIsPreparedToPlayDidChangeNotification object:_player];
|
|
[[NSNotificationCenter defaultCenter]removeObserver:self name:MMPMoviePlayerPlaybackStateDidChangeNotification object:_player];
|
|
[[NSNotificationCenter defaultCenter]removeObserver:self name:MMPMoviePlayerVideoDecoderOpenNotification object:_player];
|
|
}
|
|
|
|
@end
|