我有一个 iPad 应用程序使用变体播放列表播放 http 视频流,它具有针对不同带宽的不同比特率的多个流。其中之一是最低带宽的仅音频流。该应用程序使用AVPlayer播放视频。出于某种原因,我不能使用 MPMoviePlayerViewController。
我遇到的问题是,当视频播放器播放纯音频流时,视频显示黑屏,音频仍在播放。我认为这对用户来说很糟糕,他们不知道发生了什么。我想显示静止图像来代替视频播放器。像这样
有什么方法可以检测播放器何时切换到不同的流?或者检测流是否是纯音频的?
我有一个 iPad 应用程序使用变体播放列表播放 http 视频流,它具有针对不同带宽的不同比特率的多个流。其中之一是最低带宽的仅音频流。该应用程序使用AVPlayer播放视频。出于某种原因,我不能使用 MPMoviePlayerViewController。
我遇到的问题是,当视频播放器播放纯音频流时,视频显示黑屏,音频仍在播放。我认为这对用户来说很糟糕,他们不知道发生了什么。我想显示静止图像来代替视频播放器。像这样
有什么方法可以检测播放器何时切换到不同的流?或者检测流是否是纯音频的?
由于我遇到了这个问题并且之前的答案不完整(不包括直播案例) - 这是我在此基础上的改进:
if ([keyPath isEqual:@"tracks"])
{
BOOL hasVideoTrack = NO;
for (AVPlayerItemTrack* track in [[yourPlayer.currentItem] tracks])
{
if ([track.assetTrack.mediaType isEqual:AVMediaTypeVideo])
{
hasVideoTrack = YES;
break;
}
}
if (hasVideoTrack)
{
// Remove audio only view
} else {
// Show audio only view
}
}
请注意 - 但是,这只会让您显示本地仅音频屏幕。播放实时流时 - 仅音频的艺术品应该来自流,所以我的代码更倾向于这个:
if ([keyPath isEqualToString:@"timedMetadata"] == YES){
for (AVMetadataItem *metadata in self.player.currentItem.timedMetadata) {
if ([[metadata commonKey] isEqualToString:@"artwork"]) {
UIImage *overlayImage = [UIImage imageWithData:metadata.dataValue];
UIImageView *overlayImageView = [[UIImageView alloc] initWithImage:overlayImage];
overlayImageView.contentMode = UIViewContentModeScaleAspectFit;
// If an audio only slide is already there, make it disappear.
[self hideAudioOnlySlide];
self.audioOnlyView = overlayImageView;
[self showAudioOnlySlide];
self.audioOnlyView.size = _playerView.size;
break;
}
}
}else if ([keyPath isEqualToString:@"tracks"] == YES){
NSArray *tracks = self.player.currentItem.tracks;
if ([self.player.currentItem hasVideoTracks] == NO) {
// Check if there is timed metadata with artwork that indicates audio only is handled at the stream level.
BOOL hasAudioOnlyFromStream = NO;
for (AVMetadataItem *metadata in self.player.currentItem.timedMetadata) {
if ([[metadata commonKey] isEqualToString:@"artwork"]) {
hasAudioOnlyFromStream = YES;
break;
}
}
// If we don't have audio only slide from the stream - carry on to show audio only slide.
//Otherwise - this is handled by the timed metadata check for artwork.
if (hasAudioOnlyFromStream == NO) {
[self showAudioOnlySlide];
}
} else {
[self hideAudioOnlySlide];
}
}
添加观察代码:
[item addObserver:self forKeyPath:@"timedMetadata" options:0 context:NULL];
[item addObserver:self forKeyPath:@"tracks" options:0 context:NULL];
删除观察代码:
@try {
[item removeObserver:self forKeyPath:@"timedMetadata"];
[item removeObserver:self forKeyPath:@"tracks"];
}
hasVideoTracks 代码(在 AVPlayerItem 上的类别内):
- (BOOL)hasVideoTracks{
BOOL hasVideoTracks = NO;
for (AVPlayerItemTrack* track in [self tracks]){
if ([track.assetTrack.mediaType isEqual:AVMediaTypeVideo]){
hasVideoTracks = YES;
break;
}
}
return hasVideoTracks;
}
笔记:
您可以为 AVPlayerItem 中的轨道属性设置观察者。
[yourPlayer.currentItem addObserver:self forKeyPath:@"tracks" options:0 context:nil];
比您需要实现每次更改曲目时都会调用的方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqual:@"tracks"])
{
for (AVPlayerItemTrack* track in [[yourPlayer.currentItem] tracks])
{
if ([track.assetTrack.mediaType isEqual:AVMediaTypeAudio])
{
// Audio track available
}
if ([track.assetTrack.mediaType isEqual:AVMediaTypeVideo])
{
// Video track available
}
}
}
}
有一些调整空间,看看NSKeyValueObserving 协议和AVPlayerItem
掌握现代媒体播放(WWDC 2014):
AVPlayer and AVPlayerItem
Deciding when to show audio-only UI
// Inside -observeValueForKeyPath:ofObject:change:context: implementation...
if (presentationSizeObservationContext == context) {
// Check if new presentation size is CGSizeZero.
CGSize size = change[NSKeyValueChangeNewKey].sizeValue;
if (CGSizeEqualToSize(size, CGSizeZero)) {
for (AVPlayerItemTrack *playerItemTrack in playerItem.tracks) {
AVAssetTrack *track = playerItemTrack.assetTrack;
if ([track hasMediaCharacteristic:AVMediaCharacteristicAudible]) {
// Show audio-only UI.
}
}
}
}