Showing posts with label avplayer. Show all posts
Showing posts with label avplayer. Show all posts

Tuesday, September 11, 2018

Convert NSDate from AVDateRangeMetadataGroup to CMTime

Leave a Comment

AVPlayer primarily measures times with CMTime. However when using an AVPlayerItemMetadataCollector to collect date range metadata from an HLS stream, the resulting AVDateRangeMetadataGroups hold NSDates instead of CMTimes.

My problem is I want to add a boundary time observer to the player so I know when the playback head actually enters the date range, but you can only add a boundary time observer using CMTime. AVPlayer and AVPlayerItem can seek to a Date and can give me the current playback head as a Date, but I don't see any way to convert a given Date into a CMTime that I can use to set a boundary time observer.

In theory if I knew what the EXT-X-PROGRAM-DATE-TIME tag said I could calculate a CMTime offset myself, but I don't see a way to retrieve that.

The only other thought I had was to retrieve both currentTime and currentDate from the AVPlayerItem, then calculate the relative offset between currentDate and my target date and apply that to the CMTime. But a problem with this solution is the currentDate and currentTime won't represent the exact same time. The delta is presumably going to be very small, but it won't be zero (unless the rate itself is zero).

1 Answers

Answers 1

I hope it is the easiest solution: You can try using CMTime.init(seconds: Double, preferredTimescale: CMTimeScale) You can calculate the seconds from your NSDate objects and that way pass them into CMTime's function. This might sound trivial but hope it helps.

Also as I mentioned above to know when currentTime is not in the nil state try overriding the property and set a property observer on it and put a print statement into your override. That way you will see in the debug console whenever it will change state. You can then trace it and set brakepoints to it.

Read More

Thursday, July 12, 2018

AVPlayerLayer in UICollectionViewCell, or how to load gifs as WhatsApp

Leave a Comment

In the app I have a UICollectionView with item size so that about ~20 items are visible on the screen at the same time. The content that I want to display in each cell is Gif image downloaded from Giphy / Tenor.

However, I realized that gif files take much more space (and time to load) than the relative mp4 files that both Tenor and Giphy provide for each animated image, which is actually obvious, cause mp4 file format has a compressing logic and stuff like that. Sorry if I use wrong terms.

In order to have list loaded faster I decided to switch using UIImageView with GIF images to AVPlayerLayer, cause mp4 file is like ~10x lighter than GIF image. But I faced with the performance issue similar to what described HERE. The flow is mostly the same, I have 20+ items visible at the same time, but because of hardware limitations it only shows 16 videos. I couldn't find any workaround or any other frameworks that would allow to have more than 16 AVPlayerLayer showing video at the same time.

I'm wondering how WhatsApp application works and handles this logic. It also has GIF selection from Tenor. I already checked and figured out that WhatsApp downloads small video files and not gif images. That's why it loads very fast. But I have no idea how they can show 20+ items at the same time. HERE is how that works in WhatsApp - https://media.giphy.com/media/33E84h3RAVn0vQWZak/giphy.gif. Also, I notices during scroll the small static previews are showing, but I don't see the app making requests for it. Probably they gets a first frame of the gif on the fly without any delays in main thread.

I also tried that, but even if I make every single stuff in background thread and the only line on the main thread is "self.imageView.image = myImage", it anyway is lugging a little bit if I have 8 items in the row for example and scrolling very fast.

I see only 2 possible solutions to have it loads fast (so we definitely need to load mp4 instead of gifs), and scroll smooth and without lugs:
1. WhatsApp uses its own custom Video Core to display video in the UICollectionViewCell .
2. WhatsApp downloads video to speed up the download process but then encodes mp4 file to gif one on the fly and use regular animated UIImageView to show the output gif file. However I was not able to have this flow working very fast without lugging during 'massive' scrolling

Any thoughts on how to implement the same to make it works fast and smooth as in WhatsApp? I'm unable to check how it handles the downloaded info, but for sure it downloads mp4 files and not gif ones.

0 Answers

Read More

Tuesday, May 22, 2018

iOS 11 AVPlayer crash when KVO

Leave a Comment

I got a weird crash when using AVPlayer to play a remote video. From the crash log on Fabric, the App crash on system thread (com.apple.avfoundation.playerlayer.configuration). The crash log is below:

Crashed: com.apple.avfoundation.playerlayer.configuration 0  libsystem_kernel.dylib         0x1839ac2e8 __pthread_kill + 8 1  libsystem_pthread.dylib        0x183ac12f8 pthread_kill$VARIANT$mp + 396 2  libsystem_c.dylib              0x18391afbc abort + 140 3  libsystem_malloc.dylib         0x1839e3ce4 szone_size + 634 4  QuartzCore                     0x187ed75e8 -[CALayer dealloc] + 72 5  QuartzCore                     0x187e75d90 CA::Transaction::commit() + 1052 6  AVFoundation                   0x18973b4a8 -[AVPlayerLayer observeValueForKeyPath:ofObject:change:context:] + 684 7  Foundation                     0x1847a2894 NSKeyValueNotifyObserver + 304 8  Foundation                     0x1847bc364 -[NSObject(NSKeyValueObserverRegistration) _addObserver:forProperty:options:context:] + 204 9  Foundation                     0x1847bc13c -[NSObject(NSKeyValueObserverRegistration) addObserver:forKeyPath:options:context:] + 124 10 AVFoundation                   0x189760714 -[AVPlayer addObserver:forKeyPath:options:context:] + 204 11 AVFoundation                   0x189890414 -[AVKVODispatcher startObservingValueAtKeyPath:ofObject:options:usingBlock:] + 136 12 AVFoundation                   0x18989189c -[AVKVODispatcher(LegacyCallbackMethod) startObservingObject:weakObserver:forKeyPath:options:context:] + 152 13 AVFoundation                   0x18973aef4 -[AVPlayerLayer _startObservingPlayer:] + 328 14 libdispatch.dylib              0x183816a54 _dispatch_call_block_and_release + 24 15 libdispatch.dylib              0x183816a14 _dispatch_client_callout + 16 16 libdispatch.dylib              0x18382096c _dispatch_queue_serial_drain$VARIANT$mp + 528 17 libdispatch.dylib              0x1838212fc _dispatch_queue_invoke$VARIANT$mp + 340 18 libdispatch.dylib              0x183821d20 _dispatch_root_queue_drain_deferred_wlh$VARIANT$mp + 404 19 libdispatch.dylib              0x18382a03c _dispatch_workloop_worker_thread$VARIANT$mp + 644 20 libsystem_pthread.dylib        0x183abef1c _pthread_wqthread + 932 21 libsystem_pthread.dylib        0x183abeb6c start_wqthread + 4 

Notice: all of crash happened on iOS11

Does anybody have idea why this crash occured?

1 Answers

Answers 1

From your stack trace, I noticed that AVPlayerLayer observeValueForKeyPath:ofObject:change:context: seems to be the cause of your issue. Hence I believe you must be implementing KVO for AVPlayer.

In which case, note two points:

  1. With the new Key-Value-Observing iOS 11 API you have relaxed requirements, however these requirements for not having to deregister from observations only apply under the following conditions:

Relaxed Key-Value Observing Unregistration Requirements

• The object must be using KVO autonotifying, rather than manually calling -will and -didChangeValueForKey: (i.e. it should not return NO from +automaticallyNotifiesObserversForKey:).

• The object must not override the (private) accessors for internal KVO state.

See here to see this being implemented in the new API with the old API addObserver and removeObserver methods. Note that the documentation is not very helpful for the new API as yet because it still is based on the old KVO implementation. But, as you can see deregistering happens automatically on deinit.

AVFoundation hides the implementation of AVPlayer for KVO support (it's a private framework), but it is likely that these relaxed requirements do not apply for AVPlayer. This code snippet from Apple in 2018, uses AVPlayer with the new KVO API, but still deregisters in a deinit method (confirming suspicions that AVPlayer does not meet the relaxed unregistration requirements for the new API).

Another explanation is that deregistering happens in deinit, but not necessarily done in the main thread. This is important for AVPlayer KVO.

  1. The reason this is important can be found from the docs:

General State Observations: You should register and unregister for KVO change notifications on the main thread. This avoids the possibility of receiving a partial notification if a change is being made on another thread.

In summary, if implementing KVO for AVPlayer with the new API, you need to explicitly unregister when you're done. Also, wrap your registering and unregistering code inside a DispatchQueue.main.async { } or similar variant.

I have assumed here that your key path is valid (just make sure they are dynamic properties).

Read More

Monday, April 23, 2018

AVPlayer audio stops after bitrate spike

Leave a Comment

My iOS app uses AVPlayer to decode H.264 videos with AAC audio tracks out of local device storage. Content with bit rate spikes cause audio to drop shortly (less than a second) after the spike is played, yet video playback continues normally. Playing the videos through Safari seems to work fine, and this behavior is repeatable on several models of iPhones ranging from 6s through 8 plus.

I've been looking for any messages generated, delegates called with error information, or interesting KVOs, but there's been no helpful information so far. What might I do to get some sort of more detailed information that can point me in the right direction?

1 Answers

Answers 1

Turned out that the AVPlayer was configured to utilize methods for loading data in a custom way. The implementation of these methods failed to follow the pattern of satisfying the requests completely. (Apple docs are a vague about this.) The video portion of the AVPlayer asked for more data repeatedly, so eventually all its data got pulled. However, the audio portion patiently waited for the data to come in because there were neither an error state reported nor was all the data provided -- the presumption being that it was pending.

So, in short, sounds like there's provisions in the video handling code to treat missing data as a stall of some form and to plow onward, whereas audio doesn't have that feature. Not a bad design -- if audio cuts out it's very noticeable, and it's also by far the smaller stream so it's much less likely.

Despite spending quite a few days on the problem before posting, the lack of any useful signals made it hard to chase down the problem. I eventually reasoned that if there's no error in producing output from the stream, the problem must be in the delivery of the stream, and the problem revealed itself once I started tweaking the data loading code.

Read More

Monday, July 10, 2017

Web-based MP4 refusing to play in Safari 10.1.1 (standard setup) but does play Chrome 58 (standard setup)?

Leave a Comment

Ok, so I have noticed that somehow Safari (Version 10.1.1 (12603.2.4), using a standard setup with no add-on's or settings modifications) sometimes refuses to play .mp4 files that Chrome (Version 58.0.3029.110 (64-bit), also a standard setup) seems to play without a problem.

I discovered the issue since I am building a macOS Cocoa Application with Xcode that helps me browse WayBack media files. It's a hobby project to help me get more familiar with coding in Swift. Since it refused to play a lot of MP4 files I decided to try these links in Safari and noticed they failed as well. Then, trying Chrome, they all work.

My OS = macOS 10.12.5 (Sierra)

This video is such an example.

Does anyone know why a basic Safari setup apparently is unable to play the mp4?

NB: While I know that the MP4 file from the given link (below) does work in Safari if I play it directly from the original source (so not retrieving it from the WayBack server) I also need to understand why it is refusing to play via the given link and how to fix that.

Could it have something to do with the link being an HTTPS link, or might it it be a macOS issue? (As I have found that the AVPlayer of my macOS Cocoa App also refuses to play these videos).

Sorry to post such a broad question. I've searched online for similar posts to find out what the problem might be. I've found that there can be a lot of different reasons as to why browsers might or might not play videos.

A lack of in-depth knowledge prevents me from understanding how to properly troubleshoot this issue in a correct manner. It also prevented me from being able to fine-tune the question any further.

0 Answers

Read More

Friday, July 7, 2017

AVPlayer libBacktraceRecording.dylib macOS App Crash

Leave a Comment

I'm currently developing a macOS application and I get a strange error from time to time. I use AVPlayer to stream a live video. Everything works fine but after some time of streaming suddenly the app crashed with the following error:

libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke:     0x10019fe32 <+0>:  pushq  %rbp     0x10019fe33 <+1>:  movq   %rsp, %rbp ->  0x10019fe36 <+4>:  movq   0x20(%rsi), %rax     0x10019fe3a <+8>:  movq   0x20(%rdi), %rcx     0x10019fe3e <+12>: cmpq   0x8(%rcx), %rax     0x10019fe42 <+16>: sete   %al     0x10019fe45 <+19>: popq   %rbp     0x10019fe46 <+20>: retq 

And the message in the console log looks like this:

2017-05-02 14:02:36.909336+0200 SampleApp[3460:236302] sendMessageWithCategory: Failed to get remote object proxy: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.rtcreportingd" UserInfo={NSDebugDescription=connection to service named com.apple.rtcreportingd} 2017-05-02 14:02:36.909421+0200 SampleApp[3460:236302] initWithSessionInfo: XPC connection interrupted 2017-05-02 14:02:36.909548+0200 SampleApp[3460:236302] sendMessageWithCategory: Failed to get remote object proxy: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.rtcreportingd" UserInfo={NSDebugDescription=connection to service named com.apple.rtcreportingd} (lldb)   

0 Answers

Read More

Saturday, June 17, 2017

Achieve smooth video scrubbing with AVPlayer

Leave a Comment

I am trying to achieve smooth video scrubbing with AVPlayer through UISlider I have searched and it seems Apple has a Technical Q&A and explained how to achieve this, but my problem is how should I use this method and change player current time with a UISlider: stopPlayingAndSeekSmoothlyToTime(newChaseTime:CMTime)

Here is my code :

//Play Intro Movie let videoURL = Bundle.main.url(forResource: "intro", withExtension: "mp4") player = AVPlayer(url:videoURL!) let playerLayer = AVPlayerLayer(player: player) playerLayer.frame = self.view.frame view.layer.addSublayer(playerLayer) //videoPlayer.play()   view.addSubview(slider) slider.maximumValue = 0 slider.maximumValue = Float(CMTimeGetSeconds((player.currentItem?.asset.duration)!)) 

Here is Apple sample code :

func stopPlayingAndSeekSmoothlyToTime(newChaseTime:CMTime)     {         player.pause()          if CMTimeCompare(newChaseTime, chaseTime) != 0         {             chaseTime = newChaseTime;              if !isSeekInProgress             {                 trySeekToChaseTime()             }         }     }         func trySeekToChaseTime()     {         if playerCurrentItemStatus == .unknown         {             // wait until item becomes ready (KVO player.currentItem.status)         }         else if playerCurrentItemStatus == .readyToPlay         {             actuallySeekToTime()         }     }       func actuallySeekToTime()     {         isSeekInProgress = true         let seekTimeInProgress = chaseTime         player.seek(to: seekTimeInProgress, toleranceBefore: kCMTimeZero,                           toleranceAfter: kCMTimeZero, completionHandler:             { (isFinished:Bool) -> Void in                  if CMTimeCompare(seekTimeInProgress, self.chaseTime) == 0                 {                     self.isSeekInProgress = false                 }                 else                 {                     self.trySeekToChaseTime()                 }         })     } 

2 Answers

Answers 1

Although I'm not using the same method as you do which is stopPlayingAndSeekSmoothlyToTime, I thought I should help you with the seeking action of the player.

func sliderValueChanged() {     var timeToSeek = player.currentItem?.asset.duration.seconds     timeToSeek = timeToSeek * Double(slider.value)     player.seek(to: CMTimeMake(Int64(timeToSeek), 1)) } 

Also you should set the slider.maximumValue to 1. Hope this helps.

Note: Please don't forget to handle currentItem optional value. If it is nil you should set the value 0 for timeToSeek variable.

Answers 2

On slider value change event, just call the

stopPlayingAndSeekSmoothlyToTime(CMTime.init(seconds: (player.currentItem?.asset.duration.seconds)!* slider.value, preferredTimescale: 1000))

The Apples sample code will change the player current time. You can also adjust toleranceBefore and toleranceAfter if you scrub the slider really fast.

Read More

Monday, June 5, 2017

How to parse m3u file with AVPlayer

Leave a Comment

I want to get track info from m3u file headers not from id3Tags. In AVPlayer's timedMetaData properties it is only id3Tag parsing is provided.

How can I get title, artist, url etc from following m3u file with AVPlayer

#EXTM3U #EXT-X-ALLOW-CACHE:NO #EXT-X-TARGETDURATION:11 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10,title="Dark Horse",artist="Katy Perry / Juicy J",url="song_spot=\"M\" MediaBaseId=\"1971116\" itunesTrackId=\"0\" amgTrackId=\"-1\" amgArtistId=\"0\" TAID=\"35141\" TPID=\"23894643\" cartcutId=\"0729388001\" amgArtworkURL=\"http://assets.iheart.com/images/1080/MI0003667474\" length=\"00:03:32\" unsID=\"-1\"" http://chunks.ihrhls.com/1469/GqsS8fnz2S6-301028-9984.aac #EXTINF:10,title="Dark Horse",artist="Katy Perry / Juicy J",url="song_spot=\"M\" MediaBaseId=\"1971116\" itunesTrackId=\"0\" amgTrackId=\"-1\" amgArtistId=\"0\" TAID=\"35141\" TPID=\"23894643\" cartcutId=\"0729388001\" amgArtworkURL=\"http://assets.iheart.com/images/1080/MI0003667474\" length=\"00:03:32\" unsID=\"-1\"" http://chunks.ihrhls.com/1469/spXO68wER45-301029-9984.aac #EXTINF:10,title="Dark Horse",artist="Katy Perry / Juicy J",url="song_spot=\"M\" MediaBaseId=\"1971116\" itunesTrackId=\"0\" amgTrackId=\"-1\" amgArtistId=\"0\" TAID=\"35141\" TPID=\"23894643\" cartcutId=\"0729388001\" amgArtworkURL=\"http://assets.iheart.com/images/1080/MI0003667474\" length=\"00:03:32\" unsID=\"-1\"" http://chunks.ihrhls.com/1469/RSEeNpIOEHk-301029-10031.aac 

1 Answers

Answers 1

You can get the track information using AVURLAsset

AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:url] options:nil]; NSArray *keys = @[@"playable", @"tracks", @"duration"];  [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() {      for (NSString *thisKey in keys) {          NSError *error = nil;          AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];          if (keyStatus == AVKeyValueStatusFailed) {              //handle error          }      }  }]; 

Also take a look at this answer to get title, album, etc.

Read More

Tuesday, February 14, 2017

AVPlayer slow loading

Leave a Comment

I'm using AVPlayer to play mp3 files from a remote url. i'm having some issues with the initial loading time of the mp3, it is very slow (around 5-8 sec).
i compared it with other third parties players and its much slower, i also compared it with an android player and it is also much slower.
so the problem is not with the url itself nor with the network connection..

another interesting point is that after the AVPlayer start playing the mp3, seeking is very fast (almost immediately), does that mean the player download the entire mp3 file before start playing, and thats the reason it is so slow?
can i control this behaviour? if not, any other ideas what can be the reason?

2 Answers

Answers 1

AVPlayer has some new functionality (for iOS 10+), that You can try out. I used it myself and everything was working properly.

/*!  @method        playImmediatelyAtRate:  @abstract      Immediately plays the available media data at the specified rate.  @discussion  When the player's currentItem has a value of NO for playbackBufferEmpty, this method causes the value of rate to change to the specified rate, the value of timeControlStatus to change to AVPlayerTimeControlStatusPlaying, and the receiver to play the available media immediately, whether or not prior buffering of media data is sufficient to ensure smooth playback.  If insufficient media data is buffered for playback to start (e.g. if the current item has a value of YES for playbackBufferEmpty), the receiver will act as if the buffer became empty during playback, except that no AVPlayerItemPlaybackStalledNotification will be posted.  */ - (void)playImmediatelyAtRate:(float)rate NS_AVAILABLE(10_12, 10_0); 

Additionally You can check out this variable (You can use KVO for it too):

   /*!      @property      reasonForWaitingToPlay      @abstract      Indicates the reason for waiting when the value of timeControlStatus is AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate      @discussion         When the value of timeControlStatus is AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate, this property describes why the player is currently waiting. It is nil otherwise.         You can use the value of reasonForWaitingToPlay to show UI indicating the player's waiting state conditionally.         This property is key value observable.         Possible values are AVPlayerWaitingWithNoItemToPlayReason, AVPlayerWaitingWhileEvaluatingBufferingRateReason, and AVPlayerWaitingToMinimizeStallsReason.     */      @property (nonatomic, readonly, nullable) NSString *reasonForWaitingToPlay NS_AVAILABLE(10_12, 10_0); 

Answers 2

Here, What I think about it.

That different file has different move atoms, So if in such file if move atom came first then this file plays with buffering. So this file downloaded with parts and it's playing continues with buffering.

In other case, in file move atom is at last then first whole file downloaded then it plays. So I think that is your case that's why mp3 delays with some time for downloading whole mp3.

You can change move atom into such file with coding. Check out this answer Move and fix moov atom of video recorded on phone iOS

Read More

Monday, January 16, 2017

Video played with AVPlayer has grey line to top and sides on iPhone 6S Plus

Leave a Comment

This seems to be a device specific bug on only iPhone 6S Plus.

Steps:

  1. Download AVPlayer demo sample code
  2. Adjust AVPlayerDemoPlaybackView.xib so that MPlayback View has margins around it
  3. Make content view color white

1px grey lines will appear at the top and sides.

Anyone knows how to workaround this?

I have tried to put an opaque view that obscure the top, yet the line will still appear ABOVE the opaque view!

0 Answers

Read More

Monday, August 22, 2016

How to play Facebook style video using UICollectionview

Leave a Comment

I am attempting to play video in UICollectionview Cell using ZOWVideoPlayer. Video is playing perfectly but currently my collectionview is playing all visible videos. I want to play only single completely visible video. This means the current video that is not cropped by scrolling process (pixels are not off-screen, but fully visible).

I also added code to check the complete visible cell in scrollview delegate methods but then my collectionview gets stuck for 1 or 2 seconds and then moves, so please help me out.

And Please Read question first then make Down vote.

Here is the code for check visible cell

- (void)checkVisibilityOfCell:(CustomCell *)cell inScrollView:(UIScrollView *)aScrollView {     @try {         CGRect cellRect = [aScrollView convertRect:cell.frame toView:aScrollView.superview];         if (cell.videoPlayer) {             if (CGRectContainsRect(aScrollView.frame, cellRect)){                 //Play Video             }             else{                 //Pause Video             }         }     } @catch (NSException *exception) {      } @finally {      } } 

1 Answers

Answers 1

I suspect your method is getting called more than once. As you are only checking for players existence. Why don't you add another check for the player state? So if the player is playing, just ignore it.

if (CGRectContainsRect(aScrollView.frame, cellRect) && !isPlaying) { 

isPlaying -> An enum you can add for storing player current state

Hope this make sense ;)

Read More

Monday, April 25, 2016

TVOS AVPlayerController - Content Overlay custom subviews

Leave a Comment

Depends upon the PlaybackControl Visibity How to show and hide the Custom Subview added in contentOverlayView?

I want to do like Youtube TVOS app did.

I tried with UIPressesEvent but it is not giving me the exact touch events. It is giving me these:

  override func pressesBegan(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {     for item in presses {         switch item.type {         case .Menu:             self.customViews.alpha = 0         case .PlayPause:             self.player?.pause()             self.customViews.alpha = 0         default:             self.setVisibilityToPreviewView()         }       }     }   func setVisibilityToPreviewView () { //This wont work in all cases.       if self.previewView.alpha == 1 {          self.previewView.alpha = 0       } else {          self.previewView.alpha = 1       }     } 

But with this Touch events i can only show and hide the subviews. It should be hidden when the playbackcontrol is Hidden.

If I get the PlayBackControl Visibility values I don't need to worry about hiding these subviews.

Apple is Using AVNowPlayingPlaybackControlsViewController. It is not open for developers.

So I need to find some other better way to do this.

Please guide me how to do it.

1 Answers

Answers 1

You can register a tapGesture recognizer and then set its allowPressTypes property to UIPressType.Select, something like this

let tapRecognizer = UITapGestureRecognizer(target: self, action: "onSelect:") tapRecognizer.allowedPressTypes = [NSNumber(integer: UIPressType.Select.rawValue)]; self.view.addGestureRecognizer(tapRecognizer) 

And inside your action button show or hide custom overlays.

Example: Add this code inside a view controller and on tap (selecting at empty area on front of remote, touch area) you will see a message on console.

override func viewDidLoad() {         super.viewDidLoad()         let tapGesture = UITapGestureRecognizer(target: self, action: "onSelect")         tapGesture.allowedPressTypes = [NSNumber(integer: UIPressType.Select.rawValue)]         self.view.addGestureRecognizer(tapGesture);     }     func onSelect(){         print("this is select gesture handler method");     } 

Update: Below is the code which will create AVPlayerController and will register tapGestureRecognizer to playervc.view.

override func viewDidAppear(animated: Bool) {         super.viewDidAppear(animated)         playContent()     }     func playContent(){         let urlString = "<contentURL>"         guard let url = NSURL(string: urlString) else{             return         }         let avPlayer = AVPlayer(URL: url)         let playerVC = AVPlayerViewController()         playerVC.player = avPlayer         self.playerObj = playerVC          let tapGesture = UITapGestureRecognizer(target: self, action: "onSelect")         tapGesture.allowedPressTypes = [NSNumber(integer: UIPressType.Select.rawValue)]         self.view.addGestureRecognizer(tapGesture);          playerVC.view.addGestureRecognizer(tapGesture)         self.presentViewController(playerVC, animated: true, completion: nil);      } 

Give a try to this, I think it should work fine.

Read More

Sunday, April 10, 2016

Draw button on top of AVPlayer

Leave a Comment

I have to draw a label or button on top of video relay next previous , leave comment . List of video have it, once user select one item from the table,it need to play, Once player play finished, those buttons or label should come on top of video

Here is my code :

    comPlayerControl = AVPlayerViewController()              if let player = comPlayerControl {                  let videoURL: String = "http://cdnapi.kaltura.com/p/11/sp/11/playManifest/entryId/"+selectedSubmission.transcodeRefId+"/format/applehttp/protocol/http/a.m3u8"                 let playerItem = AVPlayerItem(URL: NSURL(string: videoURL)! )                 commmentPlayer = AVPlayer(playerItem: playerItem)                 player.player = commmentPlayer                 player.view.frame = videoCell.frame                 player.view.sizeToFit()                  player.showsPlaybackControls = true                 NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(CommentsTableViewController.playerDidFinishPlaying(_:)),                     name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)                  comPlayerControl.delegate = self                 videoCell.addSubview(player.view)             }         func playerDidFinishPlaying(note: NSNotification) {     print("Video Finished")     let DynamicView=UIView(frame: CGRectMake(100, 200, 100, 100))     DynamicView.backgroundColor=UIColor.greenColor()     DynamicView.layer.cornerRadius=25     DynamicView.layer.borderWidth=2     DynamicView.layer.zPosition = 1;     comPlayerControl.view.addSubview(DynamicView) } 

requirement like this

requirement image

3 Answers

Answers 1

You're using an AVPlayerViewController, so there's no reason to access your application's window like in Alessandro Ornano's answer. Why reinvent the wheel? Every AVPlayerViewController has a contentOverlayView property which allows you to place views between the player and the controls.

First, create a new AVPlayerItem and listen for the AVPlayerItemDidPlayToEndTimeNotification notification on that item. Load the item into your player and begin playback.

Once the item completes, the selector your specified to listen for the AVPlayerItemDidPlayToEndTimeNotification notification will be called. In that selector, access the contentOverlayView directly and add your buttons:

In some view controller or other object:

let playerVC = AVPlayerViewController()  // ...  func setupPlayer {      let playerItem = AVPlayerItem(...)     playerVC.player?.replaceCurrentItemWithPlayerItem(playerItem)     NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(VC.itemFinished), name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)     self.presentViewController(playerVC, animated: true) {          self.playerVC.player?.play()     } }  func itemFinished() {     let btn = UIButton(type: .System)     btn.addTarget(self, action: #selector(VC.buttonTapped), forControlEvents: .TouchUpInside)     self.playerVC.contentOverlayView?.addSubview(btn) }  func buttonTapped() {     print("button was tapped")     // replay/comment logic here } 

You could also subclass AVPlayerViewController and do everything inside an instance of your subclass, but Apple warns against that:

Do not subclass AVPlayerViewController. Overriding this class’s methods is unsupported and results in undefined behavior.

Answers 2

I think the best way to make the avplayer buttons is explained here: IOS 8 Video Playback using AVPlayer and AVPlayerViewController .

So , I prefeer and agree with these instructions, but if you still want to add these buttons you can try to add them to the self.window

if let app = UIApplication.sharedApplication().delegate as? AppDelegate, let window = app.window {         let myFirstButton = UIButton()         myFirstButton.setTitle("test", forState: .Normal)         window.addSubview(myFirstButton)         ... } 

Answers 3

Based on the your question and from the comment/code of Ramis i have made a sample code which you may try

As mentioned by JAL the contentOverlayView should be the best option to display the control over the video in the AVPlayerController, but as per my my sample demo the contentOverlayview don't have any user interaction for the buttons or other controls, as if you check in the 3D view of the AVPlayerController it has AVTouchIgnoringView/UIView ahead of the contentOverlayView which may be problem in user interaction of contentOverlayView

So another solution is to add the overlay view in the AVPlayerViewController

func addContentOverlayView() {      OverlayView.frame = CGRectMake(0,30,AVPlayerVC.view.bounds.width, 100)     OverlayView.hidden = true     OverlayView.backgroundColor = UIColor ( red: 0.5, green: 0.5, blue: 0.5, alpha: 0.379 )      let btnNext = UIButton(frame:CGRectMake(AVPlayerVC.view.bounds.width - 60,0,60,44))     btnNext.setTitle(">>", forState:.Normal)     btnNext.addTarget(self, action:"playNext", forControlEvents:.TouchUpInside)     //        btnNext.layer.borderColor = UIColor ( red: 0.0, green: 0.0, blue: 1.0, alpha: 0.670476140202703 ).CGColor     //        btnNext.layer.borderWidth = 1.0     OverlayView.addSubview(btnNext)      let btnReplay = UIButton(frame:CGRectMake((AVPlayerVC.view.bounds.width/2)-40,0,80,44))     btnReplay.setTitle("Replay", forState:.Normal)     btnReplay.addTarget(self, action:"replayVideo", forControlEvents:.TouchUpInside)     OverlayView.addSubview(btnReplay)      let btnPrevious = UIButton(frame:CGRectMake(0,0,80,44))     btnPrevious.setTitle("<<", forState:.Normal)     btnPrevious.addTarget(self, action:"previousVideo", forControlEvents:.TouchUpInside)     OverlayView.addSubview(btnPrevious)      let btnComment = UIButton(frame:CGRectMake((AVPlayerVC.view.bounds.width/2)-70,40,140,44))     btnComment.setTitle("Comments", forState:.Normal)     btnComment.addTarget(self, action:"openComments", forControlEvents:.TouchUpInside)     OverlayView.addSubview(btnComment)      AVPlayerVC.view.addSubview(OverlayView);  }  func playNext() {     prevItem = AVPlayerVC.player?.currentItem     OverlayView.hidden = true     commmentQueuePlayer.advanceToNextItem() }  func replayVideo() {     OverlayView.hidden = true     AVPlayerVC.player?.currentItem?.seekToTime(kCMTimeZero)     AVPlayerVC.player?.play() }  func previousVideo() {     OverlayView.hidden = true     if prevItem != AVPlayerVC.player?.currentItem {         if (commmentQueuePlayer.canInsertItem(prevItem!, afterItem:AVPlayerVC.player?.currentItem)) {             //commmentQueuePlayer.insertItem(prevItem!, afterItem:AVPlayerVC.player?.currentItem)             commmentQueuePlayer.replaceCurrentItemWithPlayerItem(prevItem)             prevItem = AVPlayerVC.player?.currentItem             replayVideo()         }     } else {         replayVideo()         //Else display alert no prev video found     } }  func stopedPlaying() {     if prevItem == nil {         prevItem = AVPlayerVC.player?.currentItem     }     OverlayView.hidden = false } 

At the initial setup we set the AVPlayerController,AVQueuePlayer etc... at that time we can add the overlay on the AVPlayerController

For the previous item there is no direct available and as per documentation the item will be remove once it's next item is played , so we have two option like replaceCurrentItemWithPlayerItem or insertItem(item: AVPlayerItem, afterItem: AVPlayerItem?)

If you need to check complete code you can check it from : https://gist.github.com/Pyrolr/debb4fca8f608b1300e099a5b3547031

Note: This is just like prototype, it is not working perfectly in all the cases but it can help you in understanding the basic functionality you wnat and you can improve/optimise based on your requirements

Read More