Multiple Video Playback on iOS

01/11/2012 § 38 Comments


As usual let me start by telling you a quick story on how this post came to be…

Once upon a time..NOT. So, I was working on this project for Hyundai at nKey when we got into this screen that requires two videos playing at the same time so the user could see how a car would behave with and without a feature (like Electronic Stability Control). As an experienced developer I immediately told the customer we should merge both videos so that we could play “both at the same time” in iOS. I explained him that in order to play videos on iOS, Apple has released a long time ago the MediaPlayer.framework which according to the docs (and real life :P) isn’t able to handle more than one video playback at a time (although you can have two MPMoviePlayerController instances).

He said OK and that was what we did. However the requirements changed and we had to add a background video that plays on a loop..aaand I started to have problems coordinating all these video playbacks so that only one was playing at a time and the user wouldn’t notice it.

Fortunately nKey sent me to the iOS Tech Talk that was happening on São Paulo/BR this very monday and there I got into a talk where the Media Technologies Evangelist, Eryk Vershen was discussing the AVFoundation.framework and how it is used by the MediaPlayer.framework (aka MPMoviePlayerController) for video playback. After the talk during Wine and Cheese I got to speak to Eryk about my issue and explained him how I was thinking about handling the problem. His answer was something like “Sure! Go for it! iOS surely is capable of playing multiple videos at the same time!…Oh…and I think something around 4 is the limit”. That answer made me happy and curious so I asked him why is the MediaPlayer.framework incapable of handling multiple video playback if that wasn’t a library limitation…he told me the MPMoviePlayerController was created to present cut-scenes on games early on…that this is why on previous iOS versions only fullscreen playback was allowed and that this limitation is a matter of legacy.

When I got back to my notebook I worked on this very basic version of a video player using the AVFoundation.framework (which obviously I made more robust when I got back to the office so that we could use it on the project).

Okdq, story told. Let’s get back to work!

The AVFoundation framework provides you the AVPlayer object to implement controllers and user interfaces for single- or multiple-item playback. The visual results generated by the AVPlayer object can be displayed  in a CoreAnimation layer of class AVPlayerLayer. In AVFoundation timed audiovisual media such as videos and sounds are represented by an AVAsset object. According to the docs, each asset contains a collection of tracks that are intended to be presented or processed together, each of a uniform media type, including but not limited to audio, video, text, closed captions, and subtitles. Due to the nature of timed audiovisual media, upon successful initialization of an asset some or all of the values for its keys may not be immediately available. In order to avoid blocking the main thread, you can register your interest in particular keys and become notified when their values are available.

Having this in mind, subclass UIViewController and name this class VideoPlayerViewController. Just like the MPMoviePlayerController, let’s add a NSURL property that tells us from where we should grab our video. Like described above, add the following code to load the AVAsset once the URL is set.

 #pragma mark - Public methods  
 - (void)setURL:(NSURL*)URL {
      [_URL release];
      _URL = [URL copy];
      AVURLAsset *asset = [AVURLAsset URLAssetWithURL:_URL options:nil];
      NSArray *requestedKeys = [NSArray arrayWithObjects:kTracksKey,
                kPlayableKey, nil];
      [asset loadValuesAsynchronouslyForKeys:requestedKeys
                           completionHandler: ^{ dispatch_async(
                                    dispatch_get_main_queue(), ^{
                                 [self prepareToPlayAsset:asset
 withKeys:requestedKeys];
                           });
      }];
} 
- (NSURL*)URL {
      return _URL;
}

So, once the URL for the video is set we create an asset to inspect the resource referenced by the given URL and asynchronously load up the values for the asset keys “tracks” and “playable”. At loading completion we can operate on the AVPlayer on the main queue (the main queue is used to naturally ensure safe access to a player’s nonatomic properties while dynamic changes in playback state may be reported).

 #pragma mark - Private methods 
- (void)prepareToPlayAsset: (AVURLAsset *)asset withKeys
 (NSArray *)requestedKeys { 
     for (NSString *thisKey in requestedKeys) { 
        NSError *error = nil;  
        AVKeyValueStatus keyStatus = [asset  
             statusOfValueForKey:thisKey
                           error:&error];  
        if (keyStatus == AVKeyValueStatusFailed) {  
           return;
        } 
     }
 if (!asset.playable) {
           return;
      }
      if (self.playerItem) {
          [self.playerItem removeObserver:self forKeyPath:kStatusKey];
          [[NSNotificationCenter defaultCenter] removeObserver:self 
                   name:AVPlayerItemDidPlayToEndTimeNotification
                 object:self.playerItem];
      }
      self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
      [self.playerItem addObserver:self forKeyPath:kStatusKey 
              options:NSKeyValueObservingOptionInitial |
                      NSKeyValueObservingOptionNew
              context:
           AVPlayerDemoPlaybackViewControllerStatusObservationContext];
      if (![self player]) {
            [self setPlayer:[AVPlayer playerWithPlayerItem:self.playerItem]];
            [self.player addObserver:self forKeyPath:kCurrentItemKey 
                  options:NSKeyValueObservingOptionInitial |
                          NSKeyValueObservingOptionNew
                  context:
             AVPlayerDemoPlaybackViewControllerCurrentItemObservationContext];
      }
      if (self.player.currentItem != self.playerItem) {
             [[self player] replaceCurrentItemWithPlayerItem:self.playerItem];
      }
}

At the completion of the loading of the values for all keys on the asset that we require, we check whether loading was successfull and whether the asset is playable. If so, we set up an AVPlayerItem (representation of the presentation state of an asset that’s played by an AVPlayer object) and an AVPlayer to play the asset. Note that I didn’t add any error handling at this point. Here we should probably create a delegate and let the view controller or whoever is using your player to decide what is the best way to handle the possible errors.

Also we added some key-value observers so that we are notified when our view should be tied to the player and when the the AVPlayerItem is ready to play.

#pragma mark - Key Valye Observing

- (void)observeValueForKeyPath: (NSString*) path
                      ofObject: (id)object
                        change: (NSDictionary*)change
                       context: (void*)context {
	if (context == AVPlayerDemoPlaybackViewControllerStatusObservation
             Context) {
              AVPlayerStatus status = [[change objectForKey:
                NSKeyValueChangeNewKey] integerValue];
              if (status == AVPlayerStatusReadyToPlay) {
                   [self.player play];
              }
	} else if (context == AVPlayerDemoPlaybackViewControllerCurrentItem
             ObservationContext) {
              AVPlayerItem *newPlayerItem = [change objectForKey:
                 NSKeyValueChangeNewKey];

              if (newPlayerItem) {
                  [self.playerView setPlayer:self.player];
                  [self.playerView setVideoFillMode:
                      AVLayerVideoGravityResizeAspect];
              }
	} else {
		[super observeValueForKeyPath:path ofObject: object
                    change:change context:context];
	}
}

Once the AVPlayerItem is on place we are free to attach the AVPlayer to the player layer that displays visual output. We also make sure to preserve the video’s aspect ratio and fit the video within the layer’s bounds.

As soon as the AVPlayer is ready, we command it to play! iOS does the hard work :)

As I mentioned earlier, to play the visual component of an asset, you need a view containing an AVPlayerLayer layer to which the output of an AVPlayer object can be directed. This is how you subclass a UIView to meet the requirements:

@implementation VideoPlayerView

+ (Class)layerClass {
	return [AVPlayerLayer class];
}

- (AVPlayer*)player {
	return [(AVPlayerLayer*)[self layer] player];
}

- (void)setPlayer: (AVPlayer*)player {
	[(AVPlayerLayer*)[self layer] setPlayer:player];
}

- (void)setVideoFillMode: (NSString *)fillMode {
	AVPlayerLayer *playerLayer = (AVPlayerLayer*)[self layer];
	playerLayer.videoGravity = fillMode;
}

@end

And this is it!

Of course I didn’t add all the necessary code for building and running the project but I wouldn’t let you down! Go to GitHub and download the full source code!

About these ads

Tagged: , , , , , , , ,

§ 38 Responses to Multiple Video Playback on iOS

  • andsien says:

    Hi,

    thanks for sharing this. I just want to ask a question, is it possible to add notification if the movie has just finish playing. If yes can you explain to me how to add it.

    thanks

    • andsien says:

      Ok sir, i got, i just added a notification and added a delegate calling a function after avplayer stops playing.

      thank you very on this post.

  • Ronan says:

    Just what I was looking for. Excellent. Well done.

  • Xandru says:

    I have tried to put 6 streams on the same screen and I failed.
    First 4 streams that were loaded were shown, rest of them – ignored. Do you have any ideas?

    • Actually I have never needed to put more than 3 streams to play at once. But last time I asked this question to the Media Technologies Evangelist, Eryk Vershen, he said he believed that four or five streams were the upper limit for iOS due to its buffer capabilities.

  • vadimv says:

    Oh…. I have a request for 6 videos, and is too bad that I don’t find where to read about. On apple’s site have found nothing, only about html5 and browser which can play only one video at time.

  • Eben says:

    Thanks very much!
    When I running this app in my iPad, everything is OK, but no sound. What’s the problem? In Simulator, there is no such problem.

    • Eben,

      I can only think of two cases:

      1) You are on mute :P
      2) Something is different on the iOS version you are testing the video. What version is it?

      Have looked in resources like Stackoverflow for similiar issues? Please post the links if so.

      Thanks!

  • Dani says:

    I’ve seen and understood the four videos limit, but what should I recycle to avoid it? I have an application where several classes (which inherit from UIView) have an internal AVPlayer instance and independents AVAssets. I switch between them on certain moments and never play the videos at the same time, but I always get a blank screen when I try to play the fifth one (sometimes fourth, it could depend on other applications AVPlayers)…

    So, what should I do? Have only one AVPlayer for the whole app? recycle the AVAssets? Have only one asset and load there many tracks?

    I liked the idea of having all these AVAssets ready, because I usually need a quick switch between videos without a glitch.

    Thanks for the info

    • Actually the number of videos that can be “playing” at the same time depends on the buffer you have.

      So you have to stop a video playback and free its resources before playing the next one. Pausing the video playback is not enough since it still uses up some buffer.

      If you need to have like six videos playing at the same time, you should re-consider your application’s architecture and maybe merge two videos where it is possible (ie: side-by-side comparison videos) to reduce the number of concurrent videos.

      To avoid the glitches, you can use placeholders (poster frames) and/or loading views/spinners to smooth user experience.

      • Dani says:

        Ok. Thank you very much!!! :) I’ll rework my app to have a “pool” of two players where one will prepare one video while the alternate is playing the other, and use AVPlayer -(void)replaceCurrentItemWithPlayerItem:AVPlayerItem to recycle the players. Using this -replaceCurrentItemWithPlayerItem:, I’ve seen there is a minor glitch, so using this pool seems to be enough (using videos with duration > 0.5 secs hehe).

        My target is to show a continuous video (that’s why I cannot accept delays or white flashes). Using n AVPlayerItems, I get that continuous effect, because the videos are preloaded and ready to be played. I hope that using this pool, I’ll get this same effect.

        Do you think there could be any situation (i.e. by using other apps) where it could be not possible to use two AVPlayerItem at the same time (or even just one)?

        Thanks!

      • No, I don’t think so.

        Good luck!

      • Dani says:

        Works great! Thank you very much for your explanations :)

      • Dani says:

        Hi Cezar,

        This might be old, but you can check what I did with the pool of two AVPlayers here: https://itunes.apple.com/es/app/primeros-auxilios-faciles/id597294121?mt=8

        In case the video is really short, there could exist a minor white flash, but can be avoided with the placeholders. Anyway, I think now works reasonable nice

        Thanks again for the support you gave me

  • rpvaghela says:

    i want to use in my iphone app 5 different videos in a same view in scroll view when user can play any one of then that video will play on there screen or user can full screen then also he can do it this thing for htat what should i have to do for the iphone app.

    • Hi,

      I didn’t quite get the requirements, but I think Dani wanted to do something similar. Please refer to my conversation with him some comments ago.

      Thanks for getting in touch!

  • guirogieri says:

    Amigo parabéns pelo post!
    Estou tendo um problema que a maioria pelo que vi nos comentários tbm tem, que é rodar mais de 4 vídeos.
    Porém no meu caso estou exibindo esses vídeos em viewcontrollers diferentes e não simultaneamente.
    E mesmo assim não consegui fazer com que a partir do quarto vídeo se reproduzam.
    Sabe se de alguma maneira no unload da view eu consiga liberar p/ que as proximas viewcontrollers que possuem vídeos possam rodar?!
    Desde já obrigado.

    • Thanks!

      Although it is theoretically possible to play any number of videos at the same time, we know (by experimentation) there is a limit of 4 in the current combination of hardware/software. I already got some e-mails about people making up to 6 concurrent video playbacks. But that really depends on how the device buffer is doing and often fails =)

      You can unload views by simply calling controller.view = nil. But I don’t think that is the way to go. You should really create public methods in your custom controllers that stops the video playback and frees any memory being used by them.

      I have an app safely running 3 concurrent videos without too much playback management. The fourth though I chose to take off by merging two videos into one file.

  • Suresh says:

    I have tried running the Sample code through git Repository. But the issue is that the first video works fine But the second video plays only for few seconds and that too without any controls.. Any idea why this issue? I am using iOS SDK 6.0 and the iPhoe version is 5.1. Is that the issue?

    • Hi

      So playing without any controls is expected since only playback is supported. The videos I privided are really small, but if it is really noy playing til the end that may very well be an issue involving SDK/iOS updates.

      If I manage to alloc some time to work on this, I will update the code so any issues are fixed.

      Thanks!

  • guirogieri says:

    Cezar, lembro que usei esse seu post a um tempo atrás e foi muito útil pra mim. Hoje tbm estou usando AVPlayer para reproduzir vídeos, porém com o vídeo em loop. Estou tendo um problema que é uma pequena pausa no momento de iniciar o vídeo novamente. Coisa rápida porém perceptivel. Já chegou a trabalhar com isso alguma vez, ou saberia me dizer como posso resolver esse problema! O que estou fazendo é o mesmo que esse post. http://stackoverflow.com/questions/5361145/looping-a-video-with-avfoundation-avplayer Abraços e mto obrigado.

    • Hi guirogieri,

      I used a similiar approach on an application I developed for Hyundai.

      There are two differences:

      1. I don’t use AVPlayerItem, but AVPlayer
      2. After calling [self.player seekToTime:kCMTimeZero], I call [self.player play].

      There may be however a little pause if the device is too busy with other tasks. But you should not notice the playback restart if the app is standalone and the first and last video frames are perfectly in sync.

      Greetings,
      Cezar

  • Bahonur says:

    Hi man your post and tutorial is a great help. However i got one question. I am in a project where it is required to play a continuos video as a background of uiview. Do you have any idea or proposal how one could solve this issue? Thanks a lot.

  • Kerouac says:

    Thanks for posting this. Until I ran into this, I didn’t think this was possible on the iPhone, but your solution works great.

  • R. F says:

    Thanks very much! A question regarding “iOS” limit and Safari: Can somebody explain where the 4-video limit is coming from? Is this a restriction on the hardware or iOS Cocoa/Touch libraries?

    I tried loading multiple videos using HTML5, and I was able to load, loop and autoplay on the Safari on my laptop( as many as 8)… then I tried to do that using the Safari on the iPad, and I could only play one of them (of the same HTML5 page) at a given time. Even when I tried to load them in an app ( via a UIWebView) I could only play one of them inside a UIWebView?!

    So I thought I might have to make as many as UIWebViews that I need, and then I found this blog.

    I could only play 5 videos with your method ( 4 of your VideoPlayerViewController + 1 MPMoviePlayerController)

    I was wondering should I proceed experimenting with multiple UIWebViews or that’s pretty much the limit on the iPad? or is it the limit per app?

    I mean Safari (on laptop) obviously plays as many as 8 very smoothly and I think they are using the same framework? aren’t they?

    Sorry if that question seems naive?!

    • Hi R.F! That is actually a great question!

      So webkit has a limit of 1 video stream on iOS. Now, iOS itself doesn’t have a documented limit. It happens this limit is (empirically) four due to amount of buffers iOS can keep active. I say “it happens” because you may be able to have more than 4 videos playing at the same time, but that is very unlikely. So even if you manage to get 5 videos, you should not suppose your app will be able to stick with that number on all devices and even app executions.

  • Mike says:

    Hi Cezar,
    I have a general question about this and I would REALLY appreciate your advice and expertise. I am a designer/UX guy and I am interested in creating an app that allows users to record multiple videos of themselves. One layered on top of another like a recording studio for audio, but this would be for video. So for example, I could sing a song on one video then have 3 other videos of me harmonizing/singing to it. So at the end I could see all 4 videos playing at the same time, each in its own quadrant of the screen. So I would record one track, then after its done, I would record another while the first one plays back, and so on… Is this possible. Thank you in advance.

    Mike from NYC, NY

    • Hi Mike.

      To be honest I never worked on an app that records and plays video at the same time, nor with video merging. But yes, I think it is possible since iOS has low level APIs for video recording that doesn’t show the camera on screen, as well as APIs for video editing.

      Regards,
      Cezar

  • Ry says:

    great, helped me a lot!

  • Rakesh says:

    Hi Cezar,

    This is the awesome code and it really help me a lot. I am using this code but I stuck in a problem. I want to play mute videos. Means I don’t want to use sound of multiple videos. Can you please help me to sort out this problem. It will really helpful.

    Thanks
    Rakesh

  • Mark Tucker says:

    Hi Cezar

    I was wondering if it is possible to play four streamed videos on mute and one audio stream at them same time or would this be to much for the player to buffer at once ?

    Thanks

    Mark T

  • lephuocdai says:

    Thank you, Cezar
    How about playing a video from a library (or maybe streaming from server) and recording by the front camera simultaneously?
    Will it be the same way as your example?

  • […] first question would there be multiple video players or just 1? I was reading: http://iosguy.com/2012/01/11/multiple-video-playback-on-ios/ where he mentions that multiple videos (max 4 or 5) can be played at one time, but the app in […]

  • Andrea says:

    neat, thanks! finally a straightforward example! great job, keep it up!

  • Everything is very open with a very clear clarification of
    the challenges. It was truly informative.

    Your site is useful. Thank you for sharing!

  • Muhammad Bilal says:

    This is really great article,

    I have list of live streams, and showing these streams in horizontally in a Scrollview , When i swipe other Stream would come respectively for all Streams. My problem is when I swipe to second stream i don’t want delay, Second stream should already playing,

    Can you give me a solid solution..It would be really helpful for me because i am stuck .

    Thanks in advance. :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

What’s this?

You are currently reading Multiple Video Playback on iOS at iOS Guy.

meta

Follow

Get every new post delivered to your Inbox.

Join 38 other followers

%d bloggers like this: