ios - Reproducción de videos apilados
objective-c avfoundation (2)
Eche un vistazo a la forma en que está configurando la variable myCounter. Se establece una vez y nunca cambia hasta que se toca una vista, y luego se establece en el recuento de escenas, -1.
Además, intente ver la forma en que está configurando la var del puntero _avPlayer. Siempre se está configurando, una y otra vez, y parece que en un bucle for querría almacenar referencias en lugar de simplemente actualizar el mismo puntero al valor más reciente en la colección de escenas.
Además, de la documentación de Apple:
Puede crear números arbitrarios de capas de jugador con el mismo objeto AVPlayer. Solo la capa de jugador creada más recientemente mostrará el contenido de video en pantalla.
Por lo tanto, es posible que, dado que esté utilizando el mismo objeto AVPlayer para crear todas estas capas AVPlayer, nunca vea más de una capa de video real.
Tengo varias subvistas de la vista de imagen que se apilan en función de mis datos entrantes. Básicamente, todas estas subvistas están configuradas en una imagen o en una capa de video en función de mis datos entrantes. El problema que tengo es reproducir videos. Puedo reproducir el primer video en la pila, pero cada video posterior es solo el sonido del primer video. ¿Cómo puedo jugar cada uno en consecuencia?
las vistas se navegan a través de un evento tap como snapchat. vea abajo:
@interface SceneImageViewController ()
@property (strong, nonatomic) NSURL *videoUrl;
@property (strong, nonatomic) AVPlayer *avPlayer;
@property (strong, nonatomic) AVPlayerLayer *avPlayerLayer;
@end
@implementation SceneImageViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mySubviews = [[NSMutableArray alloc] init];
self.videoCounterTags = [[NSMutableArray alloc] init];
int c = (int)[self.scenes count];
c--;
NSLog(@"int c = %d", c);
self.myCounter = [NSNumber numberWithInt:c];
for (int i=0; i<=c; i++) {
//create imageView
UIImageView *imageView =[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
[imageView setUserInteractionEnabled:YES]; // <--- This is very important
imageView.tag = i; // <--- Add tag to track this subview in the view stack
[self.view addSubview:imageView];
NSLog(@"added image view %d", i);
//get scene object
PFObject *sceneObject = self.scenes[i];
//get the PFFile and filetype
PFFile *file = [sceneObject objectForKey:@"file"];
NSString *fileType = [sceneObject objectForKey:@"fileType"];
//check the filetype
if ([fileType isEqual: @"image"])
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//get image
NSURL *imageFileUrl = [[NSURL alloc] initWithString:file.url];
NSData *imageData = [NSData dataWithContentsOfURL:imageFileUrl];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = [UIImage imageWithData:imageData];
});
});
}
//its a video
else
{
// the video player
NSURL *fileUrl = [NSURL URLWithString:file.url];
self.avPlayer = [AVPlayer playerWithURL:fileUrl];
self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
//self.avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[self.avPlayer currentItem]];
CGRect screenRect = [[UIScreen mainScreen] bounds];
self.avPlayerLayer.frame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
[imageView.layer addSublayer:self.avPlayerLayer];
NSNumber *tag = [NSNumber numberWithInt:i+1];
NSLog(@"tag = %@", tag);
[self.videoCounterTags addObject:tag];
//[self.avPlayer play];
}
}
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(viewTapped:)];
[self.view bringSubviewToFront:self.screen];
[self.screen addGestureRecognizer:tapGesture];
}
- (void)viewTapped:(UIGestureRecognizer *)gesture{
NSLog(@"touch!");
[self.avPlayer pause];
int i = [self.myCounter intValue];
NSLog(@"counter = %d", i);
for(UIImageView *subview in [self.view subviews]) {
if(subview.tag== i) {
[subview removeFromSuperview];
}
}
if ([self.videoCounterTags containsObject:self.myCounter]) {
NSLog(@"play video!!!");
[self.avPlayer play];
}
if (i == 0) {
[self.avPlayer pause];
[self.navigationController popViewControllerAnimated:NO];
}
i--;
self.myCounter = [NSNumber numberWithInt:i];
NSLog(@"counter after = %d", i);
}
Lo que Brooks Hanes dijo es correcto, sigues anulando al avplayer. Esto es lo que sugiero que hagas:
Agregue el gesto de tap a imageView en lugar de a la pantalla (o para un enfoque más limpio use
UIButton
enUIButton
lugar):UIImageView *imageView =[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)]; [imageView setUserInteractionEnabled:YES]; // <--- This is very important imageView.tag = i; // <--- Add tag to track this subview in the view stack [self.view addSubview:imageView]; NSLog(@"added image view %d", i); UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:imageView action:@selector(viewTapped:)]; [imageView addGestureRecognizer:tapGesture];
De esta forma, en su método viewTapped:
podría obtener la etiqueta de la imagen presionada de la siguiente manera: gesture.view.tag
lugar de usar myCounter
.
- Para que el video funcione, podría crear un nuevo
AVPlayer
para cada video, pero eso podría volverse bastante costoso para la memoria. Un mejor enfoque será usarAVPlayerItem
y cambiar elAVPlayer
delAVPlayerItem
al cambiar el video.
Entonces en el ciclo for
haga algo como esto donde self.videoFiles
es una propiedad NSMutableDictionary
:
// the video player
NSNumber *tag = [NSNumber numberWithInt:i+1];
NSURL *fileUrl = [NSURL URLWithString:file.url];
//save your video file url paired with the ImageView it belongs to.
[self.videosFiles setObject:fileUrl forKey:tag];
// you only need to initialize the player once.
if(self.avPlayer == nil){
AVAsset *asset = [AVAsset assetWithURL:fileUrl];
AVPlayerItem *item = [[AVPlayerItem alloc] initWithAsset:asset];
self.avPlayer = [[AVPlayer alloc] initWithPlayerItem:item];
self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[self.avPlayer currentItem]];
}
// you don''t need to keep the layer as a property
// (unless you need it for some reason
AVPlayerLayer* avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
CGRect screenRect = [[UIScreen mainScreen] bounds];
avPlayerLayer.frame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
[imageView.layer addSublayer:avPlayerLayer];
NSLog(@"tag = %@", tag);
[self.videoCounterTags addObject:tag];
Ahora en su vista viewTapped
:
if ([self.videoCounterTags containsObject:gesture.view.tag]) {
NSLog(@"play video!!!");
AVAsset *asset = [AVAsset assetWithURL:[self.videoFiles objectForKey:gesture.view.tag]];
AVPlayerItem *item = [[AVPlayerItem alloc] initWithAsset:asset];
self.avPlayer replaceCurrentItemWithPlayerItem: item];
[self.avLayer play];
}
O use self.videoFiles
en self.videoFiles
lugar y luego no necesita self.videoCounterTags
en absoluto:
NSURL* fileURL = [self.videoFiles objectForKey:gesture.view.tag];
if (fileURL!=nil) {
NSLog(@"play video!!!");
AVAsset *asset = [AVAsset assetWithURL:fileURL];
AVPlayerItem *item = [[AVPlayerItem alloc] initWithAsset:asset];
self.avPlayer replaceCurrentItemWithPlayerItem: item];
[self.avLayer play];
}
Esa es la esencia de eso.