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.

487 lines
14 KiB

#include "PlayerNative.h"
#import "UnityAppController.h"
static void *PlayerItemStatusContext = &PlayerItemStatusContext;
static void *PlayerItemPlaybackBufferEmpty = &PlayerItemPlaybackBufferEmpty;
static void *PlayerItemLoadedTimeRangesContext = &PlayerItemLoadedTimeRangesContext;
@implementation PlayerNative
@synthesize delegate = _delegate;
- (void)dealloc
{
[self stop];
}
- (id)init
{
self = [super init];
return self;
}
- (void)setupPlayer:(NSArray *)options
{
_player = [[AVPlayer alloc] init];
for(NSString *option in options)
{
if ([option isEqual: @"flip-vertically"])
_flipVertically = true;
}
}
- (AVPlayerItem*)getPlayerItem
{
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:_url];
if (playerItem)
{
[_player replaceCurrentItemWithPlayerItem:playerItem];
[self addPlayerItemObservers:playerItem];
}
else
{
[self reportUnableToCreatePlayerItem];
return nil;
}
return playerItem;
}
- (void)setDataSource:(NSString *)path
{
_url = [NSURL URLWithString:path];
if (_url == nil)
return;
_playerItem = [self getPlayerItem];
}
- (void)play
{
if (!_playerItem)
{
if (_url == nil)
return;
_playerItem = [self getPlayerItem];
}
if (_ready)
{
[_player play];
_playing = true;
}
}
- (void)pause
{
[self.player pause];
_playing = false;
if (_ready)
{
dispatch_async(dispatch_get_main_queue(), ^{
PlayerState *newState = [[PlayerState alloc] init];
newState.state = Paused;
[_delegate mediaPlayerStateChanged:newState];
});
}
}
- (void)stop
{
_ready = false;
_hasVideoTrack = false;
_framesCounter = 0;
[self pause];
[self cleanPixelBuffer];
if (self.playerItem)
{
[self removePlayerItemObservers:self.playerItem];
[self.player replaceCurrentItemWithPlayerItem:nil];
self.playerItem = nil;
}
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
{
PlayerState *newState = [[PlayerState alloc] init];
newState.state = Stopped;
[_delegate mediaPlayerStateChanged:newState];
}
}
- (int)getDuration
{
return _duration * 1000;
}
- (void)flipPixelBuffer:(CVPixelBufferRef)buffer withHeight:(int)height
{
if (kCVReturnSuccess == CVPixelBufferLockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly))
{
const int pitch = (int)CVPixelBufferGetBytesPerRow(buffer);
unsigned char* row = (unsigned char*)malloc(sizeof(unsigned char*) * pitch);
unsigned char* low = (unsigned char*)CVPixelBufferGetBaseAddress(buffer);
unsigned char* high = &low[(height - 1) * pitch];
for (; low < high; low += pitch, high -= pitch) {
memcpy(row, low, pitch);
memcpy(low, high, pitch);
memcpy(high, row, pitch);
}
free(row);
CVPixelBufferUnlockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly);
}
}
- (CVPixelBufferRef)getPixelBuffer
{
if (_pixelBuffer != nil)
{
int width = (int)CVPixelBufferGetWidth(_pixelBuffer);
int height = (int)CVPixelBufferGetHeight(_pixelBuffer);
if (_videoSize.width != width || _videoSize.height != height)
_videoSize = CGSizeMake(width, height);
if (_flipVertically)
[self flipPixelBuffer:_pixelBuffer withHeight:height];
}
return _pixelBuffer;
}
- (void)cleanPixelBuffer
{
if (_pixelBuffer)
{
CFRelease(_pixelBuffer);
_pixelBuffer = nil;
}
}
- (int)getFramesCounter
{
if ([self isPlaying])
{
float playbackTime = CMTimeGetSeconds([_player currentTime]);
if (fabs(playbackTime - _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 = playbackTime * 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 = playbackTime / _duration;
[_delegate mediaPlayerStateChanged:newState];
});
}
_cachedPlaybackTime = playbackTime;
}
}
CMTime outputTime = [_videoOutput itemTimeForHostTime:CACurrentMediaTime()];
if([_videoOutput hasNewPixelBufferForItemTime:outputTime])
{
[self cleanPixelBuffer];
_pixelBuffer = [_videoOutput copyPixelBufferForItemTime:outputTime itemTimeForDisplay:nil];
_framesCounter++;
}
return _framesCounter;
}
- (int)getVolume
{
if (_ready)
return self.player.volume * 100;
return 0;
}
- (void)setVolume:(int)value
{
if (_ready)
self.player.volume = (float)value / 100.0;
}
- (int)getTime
{
CMTime time = kCMTimeZero;
if (_ready)
time = [_player currentTime];
return CMTIME_IS_VALID(time) ? (int)(CMTimeGetSeconds(time) * 1000) : 0;
}
- (void)setTime:(int)value
{
if (!_seeking && [self isReady])
{
float time = (float)value / 1000.0;
CMTime cmTime = CMTimeMakeWithSeconds(time, self.player.currentTime.timescale);
_seeking = true;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.player seekToTime:cmTime completionHandler:^(BOOL finished)
{
_seeking = false;
}];
});
}
}
- (float)getPosition
{
CMTime time = kCMTimeZero;
if (_ready)
time = [_player currentTime];
return CMTIME_IS_VALID(time) ? CMTimeGetSeconds(time) / _duration : 0;
}
- (void)setPosition:(float)value
{
if (_ready)
[self setTime:((_duration * value) * 1000)];
}
- (float)getPlaybackRate
{
return _player.rate;
}
- (void)setPlaybackRate:(float)value
{
[_player setRate:value];
}
- (int)getVideoWidth
{
return _videoSize.width;
}
- (int)getVideoHeight
{
return _videoSize.height;
}
- (BOOL)isPlaying
{
return _playing;
}
- (BOOL)isReady
{
return _hasVideoTrack ? _framesCounter > 0 : _ready;
}
- (void)reportUnableToCreatePlayerItem
{
NSLog(@"Unable to create AVPlayerItem.");
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
{
dispatch_async(dispatch_get_main_queue(), ^{
PlayerState *newState = [[PlayerState alloc] init];
newState.state = EncounteredError;
[_delegate mediaPlayerStateChanged:newState];
});
}
}
- (float)getLoadedDuration
{
float loadedDuration = 0.0f;
if (self.player && self.player.currentItem)
{
NSArray *loadedTimeRanges = self.player.currentItem.loadedTimeRanges;
if (loadedTimeRanges && [loadedTimeRanges count])
{
CMTimeRange timeRange = [[loadedTimeRanges firstObject] CMTimeRangeValue];
loadedDuration = CMTimeGetSeconds(CMTimeAdd(timeRange.start, timeRange.duration));
}
}
return loadedDuration;
}
- (void)addPlayerItemObservers:(AVPlayerItem *)playerItem
{
[playerItem addObserver:self
forKeyPath:NSStringFromSelector(@selector(status))
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:PlayerItemStatusContext];
[playerItem addObserver:self
forKeyPath:NSStringFromSelector(@selector(isPlaybackBufferEmpty))
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:PlayerItemPlaybackBufferEmpty];
[playerItem addObserver:self
forKeyPath:NSStringFromSelector(@selector(loadedTimeRanges))
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:PlayerItemLoadedTimeRangesContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidPlayToEndTime:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:playerItem];
}
- (void)removePlayerItemObservers:(AVPlayerItem *)playerItem
{
[playerItem cancelPendingSeeks];
[playerItem removeObserver:self
forKeyPath:NSStringFromSelector(@selector(status))
context:PlayerItemStatusContext];
[playerItem removeObserver:self
forKeyPath:NSStringFromSelector(@selector(isPlaybackBufferEmpty))
context:PlayerItemPlaybackBufferEmpty];
[playerItem removeObserver:self
forKeyPath:NSStringFromSelector(@selector(loadedTimeRanges))
context:PlayerItemLoadedTimeRangesContext];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == PlayerItemStatusContext)
{
AVPlayerStatus newStatus = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
AVPlayerStatus oldStatus = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeOldKey] integerValue];
if (newStatus != oldStatus)
{
switch (newStatus)
{
case AVPlayerItemStatusUnknown:
{
NSLog(@"Video player Status Unknown");
break;
}
case AVPlayerItemStatusReadyToPlay:
{
if (!_ready)
{
NSDictionary *options = @{ (__bridge NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA),
(__bridge NSString *)kCVPixelBufferOpenGLESCompatibilityKey : @YES };
_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:options];
[[_player currentItem] addOutput:_videoOutput];
CMTime time;
if([AVPlayerItem instancesRespondToSelector:@selector(duration)])
time = [[_player currentItem] duration];
else
time = [[[[[[_player currentItem] tracks] objectAtIndex:0] assetTrack] asset] duration];
_duration = CMTIME_IS_INVALID(time) == NO ? CMTimeGetSeconds(time) : 0;
_ready = true;
_playing = true;
if([[_player currentItem] tracks].count > 0)
{
NSUInteger tracksCount = [[[[[[[_player currentItem] tracks] objectAtIndex:0] assetTrack] asset] tracksWithMediaType:AVMediaTypeVideo] count];
_hasVideoTrack = tracksCount > 0;
}
}
break;
}
case AVPlayerItemStatusFailed:
{
NSLog(@"Video player Status Failed: player item error = %@", self.player.currentItem.error);
NSLog(@"Video player Status Failed: player error = %@", self.player.error);
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
{
dispatch_async(dispatch_get_main_queue(), ^{
PlayerState *newState = [[PlayerState alloc] init];
newState.state = EncounteredError;
[_delegate mediaPlayerStateChanged:newState];
});
}
break;
}
}
}
}
else if (context == PlayerItemPlaybackBufferEmpty)
{
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
{
dispatch_async(dispatch_get_main_queue(), ^{
PlayerState *newState = [[PlayerState alloc] init];
newState.state = Opening;
[_delegate mediaPlayerStateChanged:newState];
});
}
}
else if (context == PlayerItemLoadedTimeRangesContext)
{
float loadedDuration = [self getLoadedDuration];
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
{
dispatch_async(dispatch_get_main_queue(), ^{
PlayerState *newState = [[PlayerState alloc] init];
newState.state = Buffering;
newState.valueFloat = loadedDuration;
[_delegate mediaPlayerStateChanged:newState];
});
}
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)playerItemDidPlayToEndTime:(NSNotification *)notification
{
if (notification.object != _player.currentItem)
return;
if ([_delegate respondsToSelector:@selector(mediaPlayerStateChanged:)])
{
dispatch_async(dispatch_get_main_queue(), ^{
PlayerState *newState = [[PlayerState alloc] init];
newState.state = EndReached;
[_delegate mediaPlayerStateChanged:newState];
});
}
}
@end