简体   繁体   中英

AVPlayer Live stream how to get power for audio level metering

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];
        }
    }

}

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

  1. the tracks property on your AVURLAsset never becoming available
  2. not calling you back when you observe and attach the tap to your playerItem.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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM