My app currently plays 2 videos at the same time, functionally, but I all the SO answers i've seen utilizes lots of Key-Value code. Is it bad/wrong if I just do the bare minimum listed below?
viewdidload
AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL URLWithString:firstVideo.videoURL]];
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
self.player1 = [AVPlayer playerWithPlayerItem:item];
[topPlayer setMovieToPlayer:self.player1];
AVURLAsset *asset2 = [AVURLAsset assetWithURL:[NSURL URLWithString:secondVideo.videoURL]];
AVPlayerItem *item2 = [AVPlayerItem playerItemWithAsset:asset2];
self.player2 = [AVPlayer playerWithPlayerItem:item2];
[bottomPlayer setMovieToPlayer:self.player2];
((AVPlayerLayer *)[self.topPlayer layer]).videoGravity = AVLayerVideoGravityResizeAspectFill;
((AVPlayerLayer *)[self.bottomPlayer layer]).videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.player1 play];
[self.player2 play];
The above code is all I use to play a video, and it works fine. Occasionally there is a ~1s delay, how can I wait until both the videos are ready to play, then play them both?
use KVO to observer the player's status, and play videos when both's status is ready.
[yourPlayerItem addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
and in kvo:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"status"]) {
AVPlayerStatus status = [change[NSKeyValueChangeNewKey] integerValue];
switch (status) {
case AVPlayerStatusUnknown:
//do something
break;
case AVPlayerStatusReadyToPlay:
{
//check which avplayer is ready, by check the parametric object isEqual:yourPlayerItem
//use a bool value to record the ready status. after two bools are YES, then play the video
}
break;
case AVPlayerStatusFailed:
{
AVPlayerItem *playerItem = (AVPlayerItem *)object;
[self assetFailedToPrepareForPlayback:playerItem.error];
}
break;
}
}
}
Related
I have to play the same local video file with the AVPlayer at the same time, and I created 4 AVPlayer instance, and the AVPlayerLayers was added to tha same layer. But the question is that 4 player were not begin at the same time. How to make them begin at the same time? here is my code:
self.players = #[].mutableCopy;
CMAudioClockCreate(kCFAllocatorDefault, &_syncClock);
AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:self.url];
for (NSInteger i = 0; i<playerNum; i++) {
AVPlayer *avPlayer = [AVPlayer playerWithPlayerItem:item.copy];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:avPlayer];
//设置模式
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
playerLayer.contentsScale = [UIScreen mainScreen].scale;
CGPoint pos = [playerOriginArr[i] CGPointValue];
playerLayer.frame = CGRectMake(pos.x, pos.y, playerSize.width, playerSize.height);
[self.playBackBgView.layer addSublayer:playerLayer];
avPlayer.masterClock = _syncClock;
[avPlayer.currentItem addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionNew context:nil];
[avPlayer play];
[self.players addObject:avPlayer];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:#"status"] && [object isKindOfClass:[AVPlayerItem class]]) {
AVPlayerItem *playerItem = (AVPlayerItem *)object;
if (playerItem.status == AVPlayerStatusReadyToPlay) {
for (AVPlayer *player in self.players) {
if (player.currentItem == playerItem) {
[player prerollAtRate:1.0 completionHandler:^(BOOL finished) {
if (finished) {
}
}];
player.automaticallyWaitsToMinimizeStalling = NO;//如果是
NSLog(#"setRate");
[player setRate:1.0 time:kCMTimeInvalid atHostTime:CMClockGetTime(_syncClock)];
}
}
}
}
}
Create just one AVPlayer and create multiple AVPlayerLayers from that.
The layers will be synchronized.
I'm creating an ios app with a streaming player maded with AvPlayer. This is my code:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self InitPlayer];
[self ReadMetaData];
}
-(void)InitPlayer{
NSURL *url = [[NSURL alloc] initWithString:#"http://www.fakeurl.com/stream"];
// create a player view controller
self.player = [AVPlayer playerWithURL:url];
player.closedCaptionDisplayEnabled = NO;
}
-(void)ReadMetaData{
[self.player.currentItem addObserver:self forKeyPath:#"timedMetadata" options:NSKeyValueObservingOptionNew context:nil];
}
- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object
change:(NSDictionary*)change context:(void*)context {
if ([keyPath isEqualToString:#"timedMetadata"])
{
AVPlayerItem* playerItem = object;
for (AVMetadataItem* metadata in playerItem.timedMetadata)
{
if([metadata.commonKey isEqualToString:#"title"]){
TitleLabel.text=metadata.stringValue;
}
}
}
}
With this code i can successfully play and pause the stream. I can also print the title of the track.
If i try to log the timedMetadata with something like this:
NSLog(#"%#",player.currentItem.timedMetadata);
I retrive that:
"<AVMetadataItem: 0x15649500, identifier=common/title, keySpace=comn, key class = __NSCFConstantString, key=title, commonKey=title, extendedLanguageTag=(null), dataType=(null), time={21888/44100 = 0.496}, duration={INVALID}, startDate=(null), extras={\n}, value=Keepin-'fake song title>"
Now my question is: for that specific stream url the timedMetadata i logged are the only metadata i can retrive? If yes how i can achive a more complex player type (something like "go to next track button","go to previous track button","an history of tracks",ecc...) ? That's the first time i work with stream data and in my expectations there was a lot of information in audio metadata. In real life seems i can get only the track title. There's a problem with my code or the stream source is poor in metadata info?
I tried to find best solution to get metadata from AVPlayer and found it:
-(IBAction) BtnGoClick:(id)sender {
NSURL *url = [[NSURL alloc] initWithString:#"http://cast.loungefm.com.ua/loungefm"];
[self setupAVPlayerForURL:url];
}
-(void) setupAVPlayerForURL: (NSURL*) url {
AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
AVPlayerItem *anItem = [AVPlayerItem playerItemWithAsset:asset];
player = [AVPlayer playerWithPlayerItem:anItem];
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
[anItem addObserver:self forKeyPath:#"timedMetadata" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == player && [keyPath isEqualToString:#"status"]) {
if (player.status == AVPlayerStatusFailed) {
NSLog(#"AVPlayer Failed");
} else if (player.status == AVPlayerStatusReadyToPlay) {
NSLog(#"AVPlayer Ready to Play");
} else if (player.status == AVPlayerItemStatusUnknown) {
NSLog(#"AVPlayer Unknown");
}
}
if ([keyPath isEqualToString:#"timedMetadata"])
{
AVPlayerItem* playerItem = object;
for (AVMetadataItem* metadata in playerItem.timedMetadata)
{
if([metadata.commonKey isEqualToString:#"title"]){
NSLog(#"%#",metadata.stringValue);
}
}
}
}
Result:
I have tried play audio from streaming. It is working fine with some URL but for some with some URL (http://listen.radionomy.com/almightyradiohindi) it fails even player status comes = AVPlayerStatusReadyToPlay. If I put same URL which is not working in browser then there it also does not work. But I think player should not show status as = AVPlayerStatusReadyToPlay. I am facing problem to manage it. For example how to I know for which streaming URL AVPlayer will work or not ?
My code :
-(void)viewDidLoad {
NSURL *url = [[NSURL alloc] initWithString:urlString];
AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
AVPlayerItem *anItem = [AVPlayerItem playerItemWithAsset:asset];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
avPlayer = [AVPlayer playerWithPlayerItem:anItem];
[avPlayer addObserver:self forKeyPath:#"status" options:0 context:nil];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == avPlayer && [keyPath isEqualToString:#"status"]) {
if (avPlayer.status == AVPlayerStatusFailed) {
NSLog(#"\n\n **** AVPlayer Failed ***\n\n");
}
else if (avPlayer.status == AVPlayerStatusReadyToPlay) {
NSLog(#"\n\n **** AVPlayer Ready to Play ****\n\n");
[avPlayer play];
}
else if (avPlayer.status == AVPlayerItemStatusUnknown) {
NSLog(#"\n\n **** AVPlayer Unknown \n\n ****");
}
}
}
when I open your audio url(http://listen.radionomy.com/almightyradiohindi) in browser it will change to (http://streaming.radionomy.com/AlmightyRadioHindi?lang=en-US%2cen%3bq%3d0.8%2cfa%3bq%3d0.6)
if you can get the exact url and url-encode it the problem will be fixed
url encoding in swift 3.0:
var originalString = "test/test"
var escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(escapedString!)
I am using AVPlayer for stream video from a URL like this :
-(void)initVideo
{
AVPlayerItem* playerItem = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#VideoFileURl",Video_BASE_URL]]];
avPlayer = [AVPlayer playerWithPlayerItem:playerItem];
AVPlayerLayer * avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:avPlayer];
avPlayerLayer.frame = self.imageUserImage.frame;
avPlayerLayer.videoGravity = AVLayerVideoGravityResize;
[self.imageUserImage.layer addSublayer:avPlayerLayer];
[self.avPlayer.currentItem addObserver:self forKeyPath:#"status" options:0 context:nil];
[avPlayer play];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (object == self.avPlayer && [keyPath isEqualToString:#"status"]) {
if (self.avPlayer.currentItem.status == AVPlayerStatusFailed) {
NSLog(#"AVPlayer Failed");
} else if (self.avPlayer.status == AVPlayerStatusReadyToPlay) {
NSLog(#"AVPlayerStatusReadyToPlay");
} else if (self.avPlayer.currentItem.status == AVPlayerItemStatusUnknown) {
NSLog(#"AVPlayer Unknown");
}
}
}
Every thing works fine & my video load as well but in some condition my video is paused forever ( hang up in a frame and doesn't continue ) !
Problem
I only receive the AVPlayerStatusFailed notification in start of a stream if there is a problem but I need to get a notification while streaming like interrupt or loose a connection while streaming.
Please help me how can I handle the network error during streaming ( interrupt in a connection & change a network status -> Like reachability
UPDATE
i find a solution , i should use AVPlayerItemPlaybackStalledNotification
but i don't know why i give this Continuous
I am trying to play video with new AVPlayerViewController introduced in XCode 6.
To play video i have done this setup.
Using local mp4 file to play video
Extended AVPlayerViewController
Player setup code :
-(void)setupPlayer
{
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"exodus_trailer" ofType:#"mp4"];
NSLog(#"File Path : %#",filePath);
AVAsset *avAsset = [AVAsset assetWithURL:[NSURL URLWithString:filePath]];
AVPlayerItem *avPlayerItem =[[AVPlayerItem alloc]initWithAsset:avAsset];
self.player = [[AVPlayer alloc]initWithPlayerItem:avPlayerItem];
[self.player addObserver:self forKeyPath:#"status" options:0 context:nil];
[self.player play];
}
KVO handling :
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == self.player && [keyPath isEqualToString:#"status"])
{
if (self.player.status == AVPlayerStatusFailed)
{
NSLog(#"AVPlayer Failed");
}
else if (self.player.status == AVPlayerStatusReadyToPlay)
{
NSLog(#"AVPlayerStatusReadyToPlay");
[self.player play];
}
else if (self.player.status == AVPlayerItemStatusUnknown)
{
NSLog(#"AVPlayer Unknown");
}
}
}
Issue :
KVO log is printing AVPlayerStatusReadyToPlay and file path seems ok. Previously view was at least showing player with all default controls, but now without any changes it started to show Quick time logo without any control. What is meaning of showing this logo ? what i am doing wrong here ?
Screenshot :
Here is exactly like what you wanted to do. The problem with your code is that you are not using file path, use this to load the file path NSURL *url = [NSURL fileURLWithPath:urlString]
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *urlString = [[NSBundle mainBundle] pathForResource:#"testfile" ofType:#"mp4"];
NSURL *url = [NSURL fileURLWithPath:urlString];
self.player = [[AVPlayer alloc] initWithURL:url];
[self.player addObserver:self forKeyPath:#"status"
options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == self.player) {
AVPlayerStatus status = [change[NSKeyValueChangeNewKey] integerValue];
if (status == AVPlayerStatusReadyToPlay) {
[self.player play];
[self.player removeObserver:self forKeyPath:#"status"];
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#end