Cambiar playerItem de AVPlayer en UITableView

Tengo una UITableView contiene una cantidad de videos para reproducir cuando se desplaza. Como las celdas en la tablaView se están reutilizando, AVPlayer solo una AVPlayer para cada fila, alguna vez. Cuando se reutiliza una celda, simplemente cambio el PlayerItem del PlayerItem de la celda llamando a [self.player replaceCurrentItemWithPlayerItem:newItem]; . Actualmente, se llama indirectamente dentro de tableView:cellForRowAtIndexPath . Al desplazarse hacia abajo, hay un retraso notable cuando ocurre la reutilización. Con un process de eliminación, he llegado a la conclusión de que el retraso es causado por replaceCurrentItemWithPlayerItem , antes de que empiece a jugar. Al eliminar esta única línea de código (evitando que el jugador obtenga un nuevo video), el retraso desaparece.

Lo que he intentado solucionarlo:

Tengo un UITableViewCell personalizado para reproducir estos videos, y he creado un método dentro de estos para inicializar con la nueva información de un object. IE, en cellForRowAtIndexPath: [cell initializeNewObject:newObject]; para realizar el siguiente método:

 //In CustomCell.m -(void)initializeNewObject:(CustomObject*)newObject { /*...*/ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ AVPlayerItem *xPlayerItem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:newObject.url]]; AVPlayer *dummy = self.player; [dummy replaceCurrentItemWithPlayerItem:xPlayerItem]; dispatch_async(dispatch_get_main_queue(), ^{ self.player = dummy; playerItem = xPlayerItem; } }/*...*/ } 

Cuando ejecuto esto, obtengo el mismo resultado que si elimine por completo la llamada para replace el elemento. Al parecer, esta function no se puede enhebrar. No estoy completamente seguro de lo que esperaba de esto. Me imagino que necesito una copy limpia de AVPlayer para que esto funcione, pero después de search un poco, encontré varios comentarios que dicen que replaceCurrentItemWithPlayerItem: no se puede llamar en un hilo por separado, lo que no tiene sentido para mí. Sé que los elementos de interfaz de usuario nunca deberían manejarse en otros subprocesss que el subprocess principal / subprocess de interfaz de usuario, pero nunca me imagino que replaceCurrentItemWithPlayerItem caiga bajo esta categoría.

Ahora estoy buscando una manera de cambiar el elemento de un AVPlayer sin el retraso, pero no puedo encontrar ninguno. Espero haber entendido mal el enhebrado de esta function, y que alguien me corrija …

EDITAR: Ahora me han informado que esta llamada ya está conectada y que esto no debería suceder realmente. Sin embargo, no veo ninguna otra explicación … Debajo está mi cellForRowAtIndexPath: Está dentro de un UITableView personalizado con los delegates configurados en self (por lo que self == tableView )

 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CustomCell *cell = [self dequeueReusableCellWithIdentifier:kCellIdentifier]; if(!cell) cell = [[[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil] objectAtIndex:0]; //Array 'data' contains all objects to be shown. The objects each have titles, url's etc. CustomVideoObject *currentObject = [data objectAtIndex:indexPath.row]; //When a cell later starts playing, I store a pointer to the cell in this tableView named 'playing' //After a quick scroll, the dequeueing cell might be the cell currently playing - resetting if(playing == cell) playing = nil; //This call will insert the correct URL, title, etc for the new video, in the custom cell [cell initializeNewObject:currentObject]; return cell; } 

El initializeNewObject que está actualmente "trabajando", sin embargo, está retrasado (esto está dentro de CustomCell.m :

 -(void)initializeNewObject:(CustomObject*)o { //If this cell is being dequeued/re-used, its player might still be playing the old file [self.player pause]; self.currentObject = o; /* //Setting text-variables for title etc. Removed from this post, but by commenting them out in the code, nothing improves. */ //Replace the old playerItem in the cell's player NSURL *url = [NSURL URLWithString:self.currentObject.url]; AVAsset *newAsset = [AVAsset assetWithURL:url]; AVPlayerItem *newItem = [AVPlayerItem playerItemWithAsset:newAsset]; [self.player replaceCurrentItemWithPlayerItem:newItem]; //The last line above, replaceCurrentItemWithPlayerItem:, is the 'bad guy'. //When commenting that one out, all lag is gone (of course, no videos will be playing either) //The lag still occurs even if I never call [self.player play], which leads me //to believe that nothing after this function can cause the lag self.isPlaying = NO; } 

El retraso ocurre exactamente en el mismo lugar cada vez. Al desplazarse rápidamente, podemos ver que el retraso corto ocurre cuando la parte superior de la celda inferior llega al centro de la pantalla. Supongo que esto no significa nada para ti, pero está claro que el rezago ocurre exactamente en el mismo lugar cada vez, es decir, cuando una nueva célula está eliminando la capa. Después de cambiar el playerItem de AVPlayer , no hago nada . No comienzo a reproducir el video hasta más tarde. replaceCurrentItemWithPlayerItem: está causando este retraso notable. La documentation indica que está cambiando el elemento en otro subprocess, pero algo en ese método está retrasando mi interfaz de usuario.

Me dijeron que usara Time Profiler en Instruments para descubrir qué es, pero no tengo idea de cómo hacerlo. Al ejecutar el perfilador y desplazarse de vez en cuando, este es el resultado (image) . Los picos de cada grupo gráfico es el retraso del que estoy hablando. El único pico extremo es (creo) cuando me desplazé hacia abajo y toqué la barra de estado para desplazarme hasta la parte superior. No sé cómo interpretar el resultado. Busqué en la stack para replace y encontré al bastardo. Se llama aquí desde el Main Thread (en la parte superior), lo que tiene sentido, con un "time de ejecución" de 50 ms, que no tengo ni idea de qué pensar. La proporción relevante del gráfico es de un time de aproximadamente 1 minuto y medio. En comparación, al comentar esa línea única y ejecutar Time Profiler nuevamente, los picos del gráfico son significativamente más bajos (pico más alto al 13%, en comparación con lo que está en la image, que es probablemente alnetworkingedor del 60-70%).

No sé qué search …

Si realiza un nivel básico de creación de perfiles, creo que puede networkingucir el problema. Para mí, tuve un problema similar en el que replaceCurrentItemWithPlayerItem estaba bloqueando el subprocess de la interfaz de usuario. Lo resolví al examinar mi código para averiguar qué línea tomaba time. Para mí, la carga de AVAsset estaba tomando time. Así que utilicé el método loadValuesAsynchronouslyForKeys de AVAsset para resolver mi problema.

Por lo tanto, puedes probar lo siguiente:

 -(void)initializeNewObject:(CustomObject*)o { //If this cell is being dequeued/re-used, its player might still be playing the old file [self.player pause]; self.currentObject = o; /* //Setting text-variables for title etc. Removed from this post, but by commenting them out in the code, nothing improves. */ //Replace the old playerItem in the cell's player NSURL *url = [NSURL URLWithString:self.currentObject.url]; AVAsset *newAsset = [AVAsset assetWithURL:url]; [newAsset loadValuesAsynchronouslyForKeys:@[@"duration"] completionHandler:^{ AVPlayerItem *newItem = [AVPlayerItem playerItemWithAsset:newAsset]; [self.player replaceCurrentItemWithPlayerItem:newItem]; }]; //The last line above, replaceCurrentItemWithPlayerItem:, is the 'bad guy'. //When commenting that one out, all lag is gone (of course, no videos will be playing either) //The lag still occurs even if I never call [self.player play], which leads me //to believe that nothing after this function can cause the lag self.isPlaying = NO; } 

En su CustomCell.m debe usar miniaturas para mostrar una image para el video de celda específico, puede agregar la miniatura a un button dentro de su celda y luego crear un delegado personalizado en CustomCell.h que se llamará cuando toque en el nuevo button creado algo así:

 @class CustomCell; @protocol CustomCellDelegate <NSObject> - (void)userTappedPlayButtonOnCell:(CustomCell *)cell; @end 

También en tu tu .h agrega la acción para el button:

 @interface CustomCell : UITableViewCell @property (strong, nonatomic) IBOutlet UIButton *playVideoButton; - (IBAction)didTappedPlayVideo; - (void)settupVideoWithURL:(NSString *)stringURL; - (void)removeVideoAndShowPlaceholderImage:(UIImage *)placeholder; @end 

También en "didTappedPlayVideo" haces lo siguiente:

 - (void)didTappedPlayVideo{ [self.delegate userTappedPlayButtonOnCell:self]; } 

el método: settupVideoWithURL se usa para settupVideoWithURL su reproductor y el método: removeVideoAndShowPlaceholderImage detendrá el video y hará que AVPlayerItem y AVPlayer sean nulas.

Ahora cuando el usuario toca una celda, usted envía la llamada nuevamente al UIViewController donde tiene la tablaView, y en el método delegado debe hacer lo siguiente:

  - (void)userTappedPlayButtonOnCell:(CustomCell *)cell{ //check if this is the first time when the user plays a video if(self.previousPlayedVideoIndexPath){ CustomCell *previousCell = (CustomCell *)[tableView cellForRowAtIndexPath:self.previousPlayedVideoIndexPath]; [previousCell removeVideoAndShowPlaceholderImage: [UIImage imageNamed:@"img.png"]]; } [cell settupVideoWithURL:@"YOURvideoURL"]; self.previousPlayedVideoIndexPath = cell.indexPath; } 

PD:

En los dispositivos más antiguos (casi todos hasta el nuevo iPad Pro) no está indicado tener varias instancias de AVPlayer . También espero que este trozo de código pueda guiarlo o ayudarlo en su búsqueda 🙂

Debe perfilar su aplicación utilizando el perfilador de time para ver dónde se está produciendo el retraso.

La documentation para replaceCurrentItemWithPlayerItem: indica claramente que se ejecuta de forma asíncrona, por lo que no debe ser el origen de su salto principal de queue.

El reemploop del artículo ocurre de forma asíncrona; observe la propiedad currentItem para averiguar cuándo ocurrirá / sucedió el reemploop.

Documentación aquí

Si todavía no puede resolverlo, publique más código y, en particular, al less su método cellForRowAtIndexPath .

ACTUALIZAR

Según el comentario de @ joey a continuación, el contenido anterior de esta respuesta ya no es válido. La documentation ya no indica que el método sea asíncrono, por lo que puede no serlo.