问题
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];
}
}
}
回答1:
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
- the
tracks
property on yourAVURLAsset
never becoming available - not calling you back when you observe and attach the tap to your
playerItem.tracks
'sassetTrack
.
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
来源:https://stackoverflow.com/questions/39947408/avplayer-live-stream-how-to-get-power-for-audio-level-metering