17 #import <AVFoundation/AVFoundation.h> 18 #import <AVKit/AVKit.h> 19 #include <glog/logging.h> 29 #define LISTEN_PLAYER_KEY_PATHS \ 31 #define LISTEN_ITEM_KEY_PATHS \ 32 { @"status", @"playbackLikelyToKeepUp", @"playbackBufferEmpty" } 38 - (id)initWithImpl:(
shaka::media::ios::AvMediaPlayer::Impl *)impl;
44 : mutex_(
"AvMediaPlayer"),
50 requested_play_(false),
67 AVPlayer *
GetPlayer() __attribute__((ns_returns_retained)) {
73 return CFBridgingRetain(layer_);
78 return CFBridgingRetain(player_);
82 clients_->AddClient(client);
86 clients_->RemoveClient(client);
96 case VideoFillMode::Stretch:
97 player_layer_.videoGravity = AVLayerVideoGravityResize;
99 case VideoFillMode::Zoom:
100 player_layer_.videoGravity = AVLayerVideoGravityResizeAspectFill;
102 case VideoFillMode::MaintainRatio:
103 player_layer_.videoGravity = AVLayerVideoGravityResizeAspect;
112 AVPlayer *player = GetPlayer();
116 std::unique_lock<SharedMutex> lock(mutex_);
117 requested_play_ =
true;
122 AVPlayer *player = GetPlayer();
126 std::unique_lock<SharedMutex> lock(mutex_);
127 requested_play_ =
false;
132 std::unique_lock<SharedMutex> lock(mutex_);
133 DCHECK(!player_) <<
"Already attached";
135 auto *str = [NSString stringWithUTF8String:src.c_str()];
138 auto *url = [NSURL URLWithString:str];
141 player_ = [AVPlayer playerWithURL:url];
145 const auto options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
147 [player_ addObserver:observer_ forKeyPath:name options:options context:nil];
149 [player_.currentItem addObserver:observer_ forKeyPath:name options:options context:nil];
155 int load_id = ++load_id_;
156 dispatch_async(dispatch_get_main_queue(), ^{
157 std::unique_lock<SharedMutex> lock(mutex_);
158 if (load_id != load_id_)
161 player_layer_ = [AVPlayerLayer playerLayerWithPlayer:player_];
162 player_layer_.frame = layer_.frame;
163 [
layer_ addSublayer:player_layer_];
165 clients_->OnAttachSource();
172 std::unique_lock<SharedMutex> lock(mutex_);
176 [player_ removeObserver:observer_ forKeyPath:name];
178 [player_.currentItem removeObserver:observer_ forKeyPath:name];
183 audio_tracks_.clear();
184 video_tracks_.clear();
185 text_tracks_.clear();
186 old_playback_state_ = VideoPlaybackState::Detached;
187 old_ready_state_ = VideoReadyState::NotAttached;
188 requested_play_ =
false;
192 for (CALayer *layer in [layer_.sublayers copy])
193 [layer removeFromSuperlayer];
195 clients_->OnDetach();
199 std::unique_lock<SharedMutex> lock(mutex_);
201 if (old_playback_state_ != new_state) {
202 const auto old_state = old_playback_state_;
203 old_playback_state_ = new_state;
204 clients_->OnPlaybackStateChanged(old_state, new_state);
205 if (new_state == VideoPlaybackState::Playing && requested_play_)
208 requested_play_ =
false;
214 std::unique_lock<SharedMutex> lock(mutex_);
217 if (old_ready_state_ != new_state) {
218 const auto old_state = old_ready_state_;
219 old_ready_state_ = new_state;
220 clients_->OnReadyStateChanged(old_state, new_state);
226 std::unique_lock<SharedMutex> lock(mutex_);
228 return audio_tracks_;
232 std::unique_lock<SharedMutex> lock(mutex_);
234 return video_tracks_;
238 std::unique_lock<SharedMutex> lock(mutex_);
244 clients_->OnError(message);
248 clients_->OnPlaybackRateChanged(old_rate, new_rate);
254 return VideoPlaybackState::Detached;
257 return VideoPlaybackState::Errored;
259 const double duration = CMTimeGetSeconds(player.currentItem.asset.duration);
260 const double time = CMTimeGetSeconds([player currentTime]);
261 if (!isnan(duration) && duration > 0 && time + 0.1 >= duration)
262 return VideoPlaybackState::Ended;
264 if (player.rate == 0)
265 return VideoPlaybackState::Paused;
266 if (player.currentItem.playbackBufferEmpty)
267 return VideoPlaybackState::Buffering;
269 return VideoPlaybackState::Playing;
274 return VideoReadyState::NotAttached;
276 if (player.currentItem.status != AVPlayerItemStatusReadyToPlay || !loaded) {
277 return VideoReadyState::HaveNothing;
279 if (player.currentItem.playbackBufferEmpty) {
283 return VideoReadyState::HaveMetadata;
286 if (!player.currentItem.playbackLikelyToKeepUp)
287 return VideoReadyState::HaveFutureData;
288 return VideoReadyState::HaveEnoughData;
292 if (loaded_ || !player_)
294 if (player_.currentItem.tracks.count == 0)
299 for (AVPlayerItemTrack *track in player_.currentItem.tracks) {
300 if ([track.assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
302 video_tracks_.emplace_back(track);
303 clients_->OnAddVideoTrack(track);
308 auto *item = player_.currentItem;
310 [item.asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
312 for (AVMediaSelectionOption *option in [group options]) {
313 std::shared_ptr<MediaTrack> track(
new AvMediaTrack(item, group, option));
314 audio_tracks_.emplace_back(track);
315 clients_->OnAddAudioTrack(track);
319 group = [item.asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
321 for (AVMediaSelectionOption *option in [group options]) {
322 std::shared_ptr<TextTrack> track(
new AvTextTrack(item, group, option));
323 text_tracks_.emplace_back(track);
324 clients_->OnAddTextTrack(track);
332 ClientList *
const clients_;
334 std::vector<std::shared_ptr<MediaTrack>> audio_tracks_;
335 std::vector<std::shared_ptr<MediaTrack>> video_tracks_;
336 std::vector<std::shared_ptr<TextTrack>> text_tracks_;
341 bool requested_play_;
345 AVPlayerLayer *player_layer_;
351 + (Class)layerClass {
355 - (void)layoutSublayers {
356 [
super layoutSublayers];
359 self.frame =
self.superlayer.bounds;
360 for (CALayer *layer in
self.sublayers)
361 layer.frame =
self.bounds;
370 - (id)initWithImpl:(
shaka::media::ios::AvMediaPlayer::Impl *)impl {
377 - (void)observeValueForKeyPath:(NSString *)keyPath
379 change:(NSDictionary<NSKeyValueChangeKey, id> *)change
380 context:(
void *)context {
381 id old = [change objectForKey:NSKeyValueChangeOldKey];
382 id new_ = [change objectForKey:NSKeyValueChangeNewKey];
383 if ([keyPath isEqual:
@"error"]) {
385 auto *error =
static_cast<NSError *
>(new_);
386 std::string str = [[error localizedDescription] UTF8String];
389 }
else if ([keyPath isEqual:
@"rate"]) {
390 auto *old_num =
static_cast<NSNumber *
>(old);
391 auto *new_num =
static_cast<NSNumber *
>(new_);
392 impl_->OnPlaybackRateChanged([old_num doubleValue], [new_num doubleValue]);
395 impl_->UpdateAndGetVideoPlaybackState();
396 impl_->UpdateAndGetVideoReadyState();
410 return impl_->GetIosView();
413 return impl_->GetAvPlayerAsPointer();
422 auto *str = [NSString stringWithUTF8String:config.audio.content_type.c_str()];
423 ret.
supported &= [AVURLAsset isPlayableExtendedMIMEType:str];
426 auto *str = [NSString stringWithUTF8String:config.video.content_type.c_str()];
427 ret.
supported &= [AVURLAsset isPlayableExtendedMIMEType:str];
436 impl_->AddClient(client);
439 impl_->RemoveClient(client);
443 AVPlayer *player = impl_->GetPlayer();
445 std::vector<BufferedRange> ret;
447 auto *ranges = player.currentItem.loadedTimeRanges;
448 ret.reserve([ranges count]);
449 for (
size_t i = 0; i < [ranges count]; i++) {
450 auto range = [[ranges objectAtIndex:i] CMTimeRangeValue];
451 auto start = CMTimeGetSeconds(range.start);
452 ret.emplace_back(start, start + CMTimeGetSeconds(range.duration));
458 return impl_->UpdateAndGetVideoReadyState();
461 return impl_->UpdateAndGetVideoPlaybackState();
464 return impl_->AudioTracks();
467 auto ret = impl_->AudioTracks();
468 return {ret.begin(), ret.end()};
471 return impl_->VideoTracks();
474 auto ret = impl_->VideoTracks();
475 return {ret.begin(), ret.end()};
478 return impl_->TextTracks();
481 auto ret = impl_->TextTracks();
482 return {ret.begin(), ret.end()};
485 const std::string &language) {
486 LOG(ERROR) <<
"Cannot add text tracks to src= content";
491 return impl_->SetVideoFillMode(mode);
494 AVPlayer *player = impl_->GetPlayer();
496 return static_cast<uint32_t
>(player.currentItem.presentationSize.width);
501 AVPlayer *player = impl_->GetPlayer();
503 return static_cast<uint32_t
>(player.currentItem.presentationSize.height);
508 AVPlayer *player = impl_->GetPlayer();
510 return player.volume;
515 AVPlayer *player = impl_->GetPlayer();
517 player.volume =
static_cast<float>(volume);
520 AVPlayer *player = impl_->GetPlayer();
527 AVPlayer *player = impl_->GetPlayer();
529 player.muted = muted;
539 AVPlayer *player = impl_->GetPlayer();
541 return CMTimeGetSeconds([player currentTime]);
546 AVPlayer *player = impl_->GetPlayer();
548 [player seekToTime:CMTimeMakeWithSeconds(time, 1000)];
551 AVPlayer *player = impl_->GetPlayer();
553 return CMTimeGetSeconds(player.currentItem.asset.duration);
558 LOG(FATAL) <<
"Cannot set duration of src= assets";
561 AVPlayer *player = impl_->GetPlayer();
568 AVPlayer *player = impl_->GetPlayer();
570 player.rate =
static_cast<float>(rate);
574 return impl_->Attach(src);
#define SHAKA_NON_COPYABLE_OR_MOVABLE_TYPE(Type)