Monitor downloaded bytes when streaming via AvPlayerItem and AvPlayer - iOS - ios

I want to monitor downloaded bytes when streaming mp3 from an URL and print it out to an IULabel. I can't find any easy method to do that.
What about "AVPlayerItemAccessLog", does it have the information? I can't figure out how to use that? Does anybody know how to get such information?
Below is my code for play the stream:
-(void)play
{
/*Allow radio to run in background*/
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
/*Gets the url fra sender*/
NSString *urlString= self.radioStation.url;
NSURL *url = [NSURL URLWithString:urlString];
/*Stop the radio if its play or have info*/
if(self.avPlayerItem)
{
[self stop];
}
/*Sets the Avplayeritem*/
self.avPlayerItem = [AVPlayerItem playerItemWithURL:url];
/*Listen for changes in the avPlayerItem*/
[self.avPlayerItem addObserver:self forKeyPath:#"status" options:0 context:nil];
/*Sets the avplayer with avplayeritem*/
self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
/*Sends loading info to the nortification*/
[[NSNotificationCenter defaultCenter] postNotificationName:#"Loading" object:self];
/*Plays the radio*/
[self.avPlayer play];
}

You need to use KVO on the AVPlayerItem to get this information. Something like this:
[self.player.currentItem addObserver:self
forKeyPath:#"loadedTimeRanges"
options:NSKeyValueObservingOptionNew
context:NULL];
And then the standard observer method:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Get the player item's loadedTimeRanges value here.
}
Don't forget to remove the observers before releasing the player item.

Related

AVPlayerItemDidPlayToEndTimeNotification firing when out of buffer

I've noticed that AVPlayerItemDidPlayToEndTimeNotification is occasionally sent when the network is deactivated and the video cannot continue playing. This is despite the video not having completed.
This can be reproduced with the following code:
#import "ViewController.h"
#import
#interface ViewController ()
#property AVPlayerItem *item;
#property AVPlayer *player;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:#"https://video-dev.github.io/streams/x36xhzz/x36xhzz.m3u8"];
self.item = [[AVPlayerItem alloc] initWithURL: url];
self.player = [[AVPlayer alloc] initWithPlayerItem:self.item];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
playerLayer.frame = self.view.bounds;
[NSNotificationCenter.defaultCenter addObserver:self selector:#selector(handleNotification:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.item];
[self.view.layer addSublayer:playerLayer];
[self.player addObserver:self forKeyPath:#"status" options:0 context:nil];
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayback
error: nil];
[self.player play];
NSLog(#"Playing");
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (object == self.player && [keyPath isEqualToString:#"status"]) {
}
}
-(void)handleNotification:(NSNotification*)notification {
NSLog(#"%#", notification.name.debugDescription);
NSLog(#"HandleNotification called at:%lld seconds. Duration on player is:%lld seconds", self.player.currentTime.value/self.player.currentTime.timescale, self.item.duration.value/self.item.duration.timescale);
}
#end
Reproduction steps are:
1. Run the app and let the video play for around 40s.
2. Kill the network on the device.
AVPlayerItemDidPlayToEndTimeNotification is fired and written to the log.
Is there any reason why I should expect this to happen? And if so, how can I distinguish between an end of play event and a failure to continue playback due to lack of buffered content?
I found same issue from AVPlayer's notification.
When AVPlayerItemFailedToPlayToEndTime is trigged, I check AVPlayerItem's position from the end.
Code sample from ModernAVPlayer library:

Why the KVO on 'status' property of AVPlayer not get called on iOS 9 and 10

Like the title says, does anyone know the reason?
Note that it does not happen on iOS 11.
My debug environment:
Xcode 9.2
iOS 9, 10, 11
My code:
In my app, I am trying to play a streaming content by AVFoundation framework. Showing the video's length and current playing time is also a feature.
The loadVideo method is called in an IBAction due to a tapped button.
Also, no special code in observeValueForKeyPath method to receive the event of KVO.
The details is listed as following:
- (void)loadVideo
{
AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:VIDEO_URL]];
Float64 duration = CMTimeGetSeconds(item.asset.duration);
[self updateTimeLabel:0.0 duration:duration];
self.player = [[AVPlayer alloc] initWithPlayerItem:item];
AVPlayerLayer *layer = (AVPlayerLayer *)self.playerView.layer;
[layer setPlayer:self.player];
[self.player addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionNew context:nil];
__weak ViewController *weakSelf = self;
self.token = [self.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, NSEC_PER_SEC)
queue:nil
usingBlock:^(CMTime time) {
Float64 currentTime = CMTimeGetSeconds(time);
[weakSelf updateTimeLabel:currentTime duration:duration];
}];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:#"status"]) {
AVPlayer *player = (AVPlayer *)object;
switch (player.status) {
case AVPlayerStatusReadyToPlay:
{
NSLog(#"player's status changed to AVPlayerStatusReadyToPlay");
}
break;
default:
NSLog(#"player's status changed");
break;
}
[player removeObserver:self forKeyPath:#"status"];
}
}
Remote content doesn't work like local content.
In iOS 9, you should be using the AVPlayerItem as the locus of information about the arrival and playback of your AVAsset from across the network, keeping track of properties such as playbackLikelyToKeepUp and the accessLog, along with notifications such as AVPlayerItemPlaybackStalled.
In iOS 10 and later, you can use the AVPlayer, but the thing to watch is its timeControlStatus.
You should observe AVPlayerItem, not AVPlayer.
[self.item addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionNew context:nil];
Apple's documentation

AVPlayer streaming works and yet it doesn't

Running Xcode 7.1.1 under El Capitan 10.11.2 an IOS 9.2 app
Trying to understand the minimum code I need to implement playback for a Video stream, and crafted this very simply piece here ... don't need the Observer strictly speaking, but it crept in so I left it.
static const NSString *ItemStatusContext;
// a class static
self.avPlayer = [AVPlayer playerWithURL:[NSURL URLWithString:#"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"]];
[self.avPlayer addObserver:self forKeyPath:#"status" options:0 context:&ItemStatusContext];
self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
avPlayerLayer.frame = CGRectMake(128, 128, 512, 386);
[self.view.layer addSublayer: avPlayerLayer];
[self.avPlayer play];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (context == &ItemStatusContext ) {
AVPlayer *thePlayer = (AVPlayer *)object;
if ([thePlayer status] == AVPlayerStatusFailed) {
NSError *error = [self.avPlayer error];
// Respond to error: for example, display an alert sheet.
NSLog(#"error %#",error);
return;
}
NSLog(#"player status %ld",(long)[thePlayer status]);
// Deal with other status change if appropriate.
}
// Deal with other change notifications if appropriate.
//[super observeValueForKeyPath:keyPath ofObject:object
// change:change context:context];
return;
}
It works, but... only on the demo stream provided by Apple, nothing else I give it plays ...
** TRIED **
Tried adding this code into the mix too, which also works with the Apple demo stream, but none of the others I have tried.
NSURL *url = [NSURL URLWithString:#"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"];
AVURLAsset *avasset = [[AVURLAsset alloc] initWithURL:url options:nil];
avPlayerItem = [[AVPlayerItem alloc] initWithAsset:avasset];
self.avPlayer = [[AVPlayer alloc] initWithPlayerItem:avPlayerItem];
.......
** MORE UPDATES ** ... reworked observer since I wasn't getting useful info from it, now it tells me the Apple m3u8 is really to play; and "fails" on everything else I try...
So .... all these fail for example ...
//self.avPlayer = [AVPlayer playerWithURL:[NSURL URLWithString:#"http://content.uplynk.com/209da4fef4b442f6b8a100d71a9f6a9a.m3u8"]];
//self.avPlayer = [AVPlayer playerWithURL:[NSURL URLWithString:#"http://content.jwplatform.com/manifests/vM7nH0Kl.m3u8"]];
//self.avPlayer = [AVPlayer playerWithURL:[NSURL URLWithString:#"http://walterebert.com/playground/video/hls/sintel-trailer.m3u8"]];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (object == self.avPlayer.currentItem && [keyPath isEqualToString:#"status"]) {
if (avPlayer.currentItem.status == AVPlayerStatusFailed) {
NSError *error = [self.avPlayer error];
// Respond to error: for example, display an alert sheet.
NSLog(#"AVPlayerStatusFailed error %#",error);
return;
}
if (avPlayer.currentItem.status == AVPlayerStatusUnknown) {
NSError *error = [self.avPlayer error];
NSLog(#"AVPlayerStatusUnknown error %#",error);
}
if (avPlayer.currentItem.status == AVPlayerStatusReadyToPlay) {
NSLog(#"AVPlayerStatusReadyToPlay");
[self.avPlayer play];
}
//[super observeValueForKeyPath:keyPath ofObject:object
// change:change context:&ItemStatusContext];
// Deal with other status change if appropriate.
}
// Deal with other change notifications if appropriate.
//[super observeValueForKeyPath:keyPath ofObject:object
// change:change context:context];
return;
}
Phew, recall something about this before; no excuses really. Managed to fix it by looking somewhere completely different; in info.plist which needs this key in it to play arbitrary streams.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
I know it is bit lax, but I leave the reader to do so some more research if they want to make their app more bullet proof that I care about right now :) Do this, use the observer code in the EDITED section and cut out the [self.avPlayer play] (line 8) in the main code your be in business.

How can I control "Done" button event of AVPlayer in iOS 8?

I need some code to capture "Done" button click event for AVPlayer in iOS. I am able to get play/pause events using the following code
-(IBAction)play_pressed:(id)sender{
NSURL *videoURL = [NSURL URLWithString:#"http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8"];
self.player = [AVPlayer playerWithURL:videoURL];
self.avPlayerViewcontroller = [AVPlayerViewController new];
self.avPlayerViewcontroller.player = self.player;
self.avPlayerViewcontroller.view.frame = self.view.frame;
[self.player addObserver:self forKeyPath:#"rate" options:0 context:nil];
[self presentViewController:self.avPlayerViewcontroller animated:YES completion:nil];
[self.player play];}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:#"rate"]) {
if ([self.player rate]) {
NSLog(#"Playing");
}
else {
NSLog(#"Paused");
}
}

avplayer is not playing the URL

I am using avplayer for play audio url, but it is not working, I don't know where i am wrong
NSString *radioURL = #"https://www.example.com";
radioPlayer = [AVPlayer playerWithURL:[NSURL URLWithString:radioURL]] ;
// [radioPlayer seekToTime:kCMTimeZero];
NSLog(#"radio player %#",radioPlayer.currentItem);
[radioPlayer play];
Any help would be appreciated.
I strongly recommended the code below to play radio streaming: please take a look also AVPlayer_Class
-(void)Play{
NSString *radioURL = #"https://www.example.com"; //this url must valid
AVPlayer *player = [[AVPlayer alloc]initWithURL:[NSURL URLWithString:radioURL]];
self.songPlayer = player; //self.songPlayer is a globle object of avplayer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[songPlayer currentItem]];
[self.songPlayer addObserver:self forKeyPath:#"status" options:0 context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == songPlayer && [keyPath isEqualToString:#"status"]) {
if (songPlayer.status == AVPlayerStatusFailed) {
NSLog(#"AVPlayer Failed");
} else if (songPlayer.status == AVPlayerStatusReadyToPlay) {
NSLog(#"AVPlayerStatusReadyToPlay");
[self.songPlayer play];
} else if (songPlayer.status == AVPlayerItemStatusUnknown) {
NSLog(#"AVPlayer Unknown");
}
}
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
// code here to play next sound file
}
Ref link is - Streaming mp3 audio with AVPlayer
I had the same issue and just realized that I wasn't retaining the player (using ARC)! So it gets deallocated and stop playing immediately after start.
You need to make sure that you have a strong property radioPlayer and use self.radioPlayer instead of radioPlayer.
You are doing good but your method need to correct on one place.Check using the below method:
NSURL *audioURL = [NSURL URLWithString:radioURL];
NSData *audioData = [NSData dataWithContentsOfURL:audioURL];
self.audioPlayer = [[AVAudioPlayer alloc] initWithData:audioData error:nil];
self.audioPlayer.numberOfLoops = -1;
self.audioPlayer.delegate = self;
[self.audioPlayer prepareToPlay];
self.audioPlayer.volume=1.0;
[self.audioPlayer autorelease];
[self.audioPlayer Play];
and add proper delegate methods for AVAudioPlayer.
Make Sure URL is AddingPercentEscapes.
NSURL *audioURL = // Your Url;
NSString *encodedString = [#"Your Url" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *myURL = [[NSURL alloc] initWithString:encodedString]
AVPlayer *player = [AVPlayer playerWithURL:myURL];
[player prepareToPlay];
player play];
Did you forgot to set the attribute App Transport Security Settings -> Allow Arbitrary Loads (Yes)?

Resources