Forma adecuada de cambiar rápidamente entre videos

Estoy creando una aplicación que muestra un cierto video basado en un evento externo, que puede requerir que el video que se reproduce cambie rápidamente, una vez por segundo o más. Sin embargo, no debe haber una brecha o retraso entre los videos.

¿Cuál es la mejor manera de hacer esto? Solo hay cuatro videos, cada uno de aproximadamente dos megabytes.

Estaba considerando crear cuatro MPMoviePlayerControllers , y tener sus vistas agregadas a la vista principal, pero ocultas, y cambiando haciendo una pausa y ocultando el video actual, y luego mostrando y reproduciendo el siguiente video. ¿Hay una solución más elegante?

Editar

Aquí hay más información para mi situación exacta:

  • Los diferentes cuadros de video comparten en su mayoría píxeles comunes, por lo que está bien que un cuadro se pegue durante el cambio, pero NO está bien que aparezcan cuadros negros.
  • Cada video tiene solo unos diez segundos y solo hay cuatro videos. Las transiciones de estado general son 1 <-> 2 <-> 3 <-> 4-> 1.
  • La reproducción de video debería ser compatible con la grabación simultánea de AVAudioRecorder. Por lo que puedo decir, MPMoviePlayerController no lo es.

El MPMoviePlayerController es un singleton. Cuatro instancias compartirán el mismo puntero, la misma vista, etc. Con el reproductor nativo, creo que solo tienes dos alternativas: una es cambiar la propiedad contentURL cuando quieres una transición. Si la latencia de esta manera es inaceptable, la otra alternativa es producir un video más largo con los clips más cortos concatenados. Puede crear saltos muy rápidos dentro del clip único y más largo mediante la configuration de currentPlaybackTime.

Deberá configurar y preconfigurar todas las secuencias de video para evitar contratimes, así que no creo que su solución MPMoviePlayerController esté demasiado lejos de la marca.

Sin embargo, esa solución específica es potencialmente problemática porque cada reproductor de películas tiene su propia interfaz de usuario. Las UI no se sincronizan entre sí, por lo que uno podría estar mostrando la barra de control, otra no; uno podría estar en modo de pantalla completa, etc. Cambiar entre ellos causará discontinuidades visuales.

Dado que parece que su conmutación de video no es necesariamente iniciada por el usuario, supongo que no le importa demasiado la interfaz de usuario del reproductor de video estándar.

Sugeriría bajar a la capa subyacente, AV Foundation. En teoría, puede crear un AVPlayerItem para cada video. Este es un object de administración de flujo sin sobrecarga de interfaz de usuario, por lo que es perfecto para lo que estás haciendo. Podría, de nuevo, en teoría, crear un AVPlayer y un AVPlayerLayer para manejar la pantalla. Cuando quisiera cambiar de una transmisión AVPlayerItem a otra, podría llamar al post replaceCurrentItemWithPlayerItem: AVPlayer para cambiar la transmisión de datos.

Hice un pequeño proyecto de testing (GitHub) para verificar esto, y, lamentablemente, la solución directa no es del todo perfecta. No hay interferencias en el flujo de video, pero en la transición de AVPlayer a AVPlayer, la capa de presentación parece mostrar brevemente el último marco visto de la película anterior con el tamaño apropiado para la siguiente película. Parece que ayuda a asignar objects separados de AVPlayer para cada película y cambiar entre ellos a una capa de jugador constante. Parece que hay un flash instantáneo de background, pero al less es un defecto sutil. Aquí está la esencia del código:

 @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 

La mitad del código está ahí solo para observar el estado de los jugadores y asegurarse de que la reproducción no se inicie hasta que todos los videos hayan sido almacenados en búfer.

La asignación de tres AVPlayerLayers separados (además de tres AVPlayers) evita cualquier tipo de flash. Desafortunadamente, un AVPlayer conectado a un AVPlayerLayer sin mostrar parece asumir que no necesita mantener un búfer de video. Cada cambio entre capas produce un tartaje de video transitorio. Entonces eso no es bueno.

Un par de cosas a tener en count al usar AV Foundation son:

1) El object AVPlayer no tiene soporte incorporado para la reproducción en bucle. Tendrá que observar para el final del video actual y search manualmente el time cero.

2) No hay interfaz de usuario en absoluto que no sea el marco de video, pero una vez más, supongo que esto podría ser una ventaja para usted.