AVPlayerItem falla con AVStatusFailed y el código de error “No se puede decodificar”

Me encuentro con un problema extraño, espero que alguien pueda ayudar.

En mi aplicación iOS, creo un video con una banda sonora personalizada usando MutableComposition combinando un video de la biblioteca de fotos del usuario y un archivo de audio del paquete de la aplicación. Luego uso un AVPlayer y AVPlayerItem para reproducir el video de vuelta al usuario usando un reproductor de video personalizado que hice.

Cada vez que se crea una nueva composición, los activos, el jugador y la composición se borran, se liberan y, básicamente, comienza desde un estado limpio e inicial.

Todo funciona bien, hasta después de exactamente 4 videos exitosos creados de esta manera, cualquier otro bash de crear el reproductor falla con el error Cannot Decode . No importa si es el mismo video que estoy recreando, no tiene relación con el tamaño / duración del video o el archivo de audio, simplemente siempre falla exactamente en el quinto bash, como un reloj. ¡Una vez que falla, siempre fallará!

Esto es raro, porque acaba de decodificar el mismo video cuatro veces sin problemas, ¿así que de repente falla? Entonces, si alguien tiene una pista, por favor avíseme.

Ok todos, tengo la respuesta a esto directamente de Apple. Utilicé una de las líneas de vida de TSI de mi desarrollador para hacer la pregunta, y resumiré la respuesta.

Hay un límite en el número de reproductores de video simultáneos que AVFoundation permitirá. Se debe a las limitaciones del hardware de iOS. El límite para dispositivos actuales es de 4 jugadores. Si crea un 5º jugador, obtendrá el error “No se puede decodificar”. No es un límite en el número de instancias de AVPlayer o AVPlayerItem. Más bien, es la asociación de AVPlayerItem con un AVPlayer lo que crea una “línea de procesamiento”, y está limitado a 4 de estos. Por ejemplo, esto causa una nueva canalización de renderizado:

 AVPlayer *player = [AVPlayer playerWithPlayerItem:somePlayerItem]; // assuming the AVPlayerItem is ready to go with an AVAsset that has been loaded 

También me advirtieron que no puede suponer que tendrá 4 tuberías disponibles para usted. Otra aplicación puede estar usando uno o más. De hecho, he visto esto suceder en un iPad, pero no estaba claro qué aplicación estaba usando una tubería.

Entonces, ahí lo tienes, estaba totalmente indocumentado, pero esa es la historia.

Me encontré con el mismo mensaje de error después de crear 4 instancias de AVPlayer, sin embargo, la solución en mi caso no era exactamente la misma. Quizás esto ayudará a cualquier otra persona que se encuentre con este problema.

Lo que finalmente descubrí es que las AVPlayers no se lanzaron cuando yo pensaba que lo eran. En mi caso, estaba presionando mi controlador AVPlayer View en un controlador de navegación. A pesar de que solo estaba creando una instancia de AVPlayer a la vez, cuando los Controladores de Vista se desconectan de un controlador de navegación, no se liberaban de inmediato. Entonces fue muy fácil para mí llegar a 4 instancias de AVPlayer antes de que se limpiaran los viejos Controladores de Vista.

No fue hasta que me aseguré de que los jugadores anteriores fueran liberados que este problema desapareció. Para completar, lancé AVPlayerItem, AVPlayer y configuré el reproductor en AVPlayerLayer en nil antes de liberarlo.

Tengo que preguntarme si existe algún límite en las instancias de AVPlayer, involuntaria o no. Una información relacionada de los documentos: https://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/02_Playback.html

“Varias capas de jugadores: puedes crear arbitrariamente muchos objetos AVPlayerLayer desde una sola instancia de AVPlayer, pero solo la capa creada más recientemente mostrará cualquier contenido de video en la pantalla”.

Este me estaba matando por completo hasta que lo descubrí, recogiendo pistas de este hilo y algunos otros. El mayor problema en mi código era que estaba creando instancias de mi controlador de reproductor de video cada vez que quería reproducir un video. Ahora, se crea una instancia una vez en el controlador primario (en este caso, mi DetailViewContoller):

 @interface DetailViewController () { VideoPlayerViewController *videoPlayerViewController; } - (void) viewDidLoad { [super viewDidLoad]; videoPlayerViewController = [[VideoPlayerViewController alloc] initWithNibName: nil bundle: nil]; } 

Cuando quiero mostrar un video, llamo al método startVideoPlayback de mi DetailViewController:

 - (void) startVideoPlayback: (NSString *)videoUID { videoPlayerViewController.videoUID = videoUID; [self presentModalViewController: videoPlayerViewController animated: YES]; } 

(NOTA: lo estoy pasando ‘videoUID’, un identificador único que se usó para crear el video en otra parte de la aplicación).

En VideoPlayerViewController (que está en gran parte copiado de la muestra AVPlayerDemo de Apple), la configuración de pantalla única (inicialización de AVPlayer, configuración de la barra de herramientas, etc.) se realiza en viewDidLoad , que ahora solo recibe una llamada una vez , y todo la configuración de video se realiza dentro de viewWillAppear , que luego llama a prepareToPlay :

 - (void) prepareToPlay { [self initScrubberTimer]; [self syncPlayPauseButtons]; [self syncScrubber]; NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; //*** Retrieve and play video at associated with this videoUID NSString *destinationPath = [documentsDirectory stringByAppendingFormat: @"/%@.mov", videoUID]; if ([self fileExists: destinationPath]) { //*** Show the activity indicator spinny thing [pleaseWait startAnimating]; [self setURL: [NSURL fileURLWithPath: destinationPath]]; //*** Get things going with the first video in this session if (isFirst) { isFirst = NO; //*** Subseqeunt videos replace the first one } else { [self.mPlayer replaceCurrentItemWithPlayerItem: [AVPlayerItem playerItemWithURL: [NSURL fileURLWithPath: destinationPath]]]; } } } 

OK, descubrí una solución, espero que esto sea útil para cualquiera que pueda tropezar con algo similar a este problema.

La solución en mi caso fue inicializar el activo para AVPlayer y AVPlayerItem en el hilo principal y asegurarme de no crear el AVPlayerLayer real antes de que playerItem y los objetos del jugador regresen con el estado “ReadyToPlay”.

Esto resultó ser difícil de aislar y todavía no sé por qué funcionó las primeras 4 veces y luego falló sistemáticamente en la quinta vez.

Hasta que no pude incluir el código, no se trataba de una línea o incluso de algunas funciones. Fue un problema complejo que no pude aislar para empezar. Gracias por los comentarios.