You need to tune and buffer all the video streams to avoid hiccups, so I don't think your MPMoviePlayerController multiple solution is too far from the mark.
However, this particular solution is potentially problematic, because each player has its own interface. User interfaces are not synchronized with each other, so you can display the control panel, and the other does not; one may be in full screen, etc. Switching between them will cause visual breaks.
Since this sounds like your video switch is not necessarily user initiated, I assume that you are not interested in the standard user interface of the video player.
I would suggest abandoning the base layer, AV Foundation. Theoretically, you can simply create an AVPlayerItem for each video. This is a flow control object with no user interface overhead, so it is perfect for what you are doing. Then you could, theoretically, create one AVPlayer and one AVPlayerLayer to handle the display. If you want to switch from one AVPlayerItem stream to another, you can call the AVPlayer message replaceCurrentItemWithPlayerItem: to exchange data with the data stream.
I did a small test project (GitHub) to test this, and, unfortunately, the direct solution is not entirely perfect. There is no video stream failure, but when switching from AVPlayer to AVPlayer, the presentation layer seems to briefly launch the previous frame of the previous movie in the size corresponding to the next movie. This seems to help highlight individual AVPlayer objects for each movie and switch between them on the player’s permanent layer. It seems to be an instant flash of the background, but at least it's a subtle defect. Here's the gist of the code:
@interface ViewController : UIViewController { NSMutableArray *players; AVPlayerLayer *playerLayer; } @property (nonatomic) IBOutlet UIView *videoView; - (IBAction) selectVideo:(id)sender; @end @implementation ViewController @synthesize videoView; - (void)viewDidLoad { [super viewDidLoad]; NSArray *videoTitles = [NSArray arrayWithObjects:@"Ultimate Dog Tease", @"Backin Up", @"Herman Cain", nil]; players = [NSMutableArray array]; for (NSString *title in videoTitles) { AVPlayerItem *player = [AVPlayer playerWithURL:[[NSBundle mainBundle] URLForResource:title withExtension:@"mp4"]]; [player addObserver:self forKeyPath:@"status" options:0 context:nil]; [players addObject:player]; } playerLayer = [AVPlayerLayer playerLayerWithPlayer:[players objectAtIndex:0]]; playerLayer.frame = self.videoView.layer.bounds; playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; [self.videoView.layer addSublayer:playerLayer]; } - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { [object removeObserver:self forKeyPath:@"status"]; for (AVPlayer *player in players) { if (player.status != AVPlayerStatusReadyToPlay) { return; } } // All videos are ready to go [self playItemAtIndex:0]; } - (void) playItemAtIndex:(NSUInteger)idx { AVPlayer *newPlayer = [players objectAtIndex:idx]; if (newPlayer != playerLayer.player) { [playerLayer.player pause]; playerLayer.player = newPlayer; } [newPlayer play]; } - (IBAction) selectVideo:(id)sender { [self playItemAtIndex:((UILabel *)sender).tag]; } @end
Half of the code is just to monitor the status of the players and ensure that playback does not start until all the videos have been buffered.
Highlighting three separate AVPlayerLayers (in addition to three AVPlayers) prevents any flash. Unfortunately, AVPlayer connected to the non-displayed AVPlayerLayer seems to suggest that it does not need to support the video buffer. Each switch between layers creates an intermittent video stutter. So nothing good.
A few things to consider when using AV Foundation:
1) The AVPlayer object does not have built-in support for looping. You will have to watch the end of the current video and manually search for zero time.
2) There is no interface in everything except the video frame, but again, I assume that this may be an advantage for you.