Reputation: 8722
I am trying to show a meter graph in my app which uses AVPlayer to stream live audio streams.
I know for AVAudioPlayer there is a way as per: Trying to understand AVAudioPlayer and audio level metering
which uses peakPowerForChannel
But AVAudioPlayer doesn't work for audio streams.
Is there something similar for AVPlayer? Or is there any other way I can get the power values from the AVPlayer?
Code:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
if (self.avplayer) {
[self.avplayer replaceCurrentItemWithPlayerItem:nil];
}
AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:@"http://broadcast.infomaniak.net/radionova-high.mp3"] options:nil];
NSArray *keys = @[@"playable"];
[avAsset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
AVPlayerItem *newItem = [[AVPlayerItem alloc] initWithAsset:avAsset];
if (!self.avplayer) {
self.avplayer = [[AVPlayer alloc] initWithPlayerItem:newItem];
} else {
[self.avplayer replaceCurrentItemWithPlayerItem:newItem];
}
[self.avplayer addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:nil];
[self.avplayer addObserver:self forKeyPath:@"rate" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:nil];
});
}];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
NSLog(@"%@ called",keyPath);
if ( [keyPath isEqualToString:@"status"]) {
[self.avplayer play];
} else if ( [keyPath isEqualToString:@"rate"]) {
if (self.avplayer.rate) {
NSLog(@"Playing...");
[[NSNotificationCenter defaultCenter] postNotificationName:@"currentPlayingChangedToPlay" object:nil];
} else {
NSLog(@"Not playing...");
[[NSNotificationCenter defaultCenter] postNotificationName:@"currentPlayingChangedToPause" object:nil];
}
}
}
Upvotes: 3
Views: 2327
Reputation: 36072
Ordinarily, you can get at the audio samples of an AVPlayer
by using an MTAudioProcessingTap
on the player item, however your asset resists this through
tracks
property on your AVURLAsset
never becoming availableplayerItem.tracks
's assetTrack
.I've never seen a good explanation for why this doesn't work for remote mp3
s or m3u8
streams, and annoyingly, it does work for other remote asset types, like aac
. Unjustly, on the video side, AVPlayerItemVideoOutput seems to work for all types of assets (barring DRM problems)!
So where does this leave you? One way is to stream the mp3 data yourself from where you can decode and play it using an AudioQueue
, and also calculate the peak power using the aforementioned MTAudioProcessingTap
. Solutions involving AudioConverter
/AudioUnit
and AVAudioConverter
/AVAudioEngine
come to mind too.
If that sounds like too much work just to replace a single property, you could look at pre-baked solutions, like StreamingKit or FreeStreamer and then calculate power. You may find a library that calculates power for you. If not, you can calculate peakPowerForChannel
using this formula
Upvotes: 4