Video fusión en segundo plano iOS

Tarea: fusionar la image del volante en un video de volantes.

Casos :

  • Cree un volante [agregue image / text del emoticón … etc]
  • Crear video

Caso 1

  • Presione el button Atrás [el usuario irá a la list de aplicaciones de la pantalla de volantes], durante esto fusionaremos flyerSnapShoot en flyerVideo.y funcionará perfectamente .
  • Al ir a la Galería telefónica, estamos viendo el video actualizado.

Case2

  • Presione el button Inicio de iPhone, estoy haciendo lo mismo que arriba, pero enfrento el siguiente error .

FAIL = Error de dominio = AVFoundationErrorDomain Code = -11800 "La operación no se pudo completar" UserInfo = 0x17266d40 {NSLocalizedDescription = La operación no pudo completarse, NSUnderlyingError = 0x172b3920 "La operación no pudo completarse (error de estado -16980). ", NSLocalizedFailureReason = Ocurrió un error desconocido (-16980)}

Código :

- (void)modifyVideo:(NSURL *)src destination:(NSURL *)dest crop:(CGRect)crop scale:(CGFloat)scale overlay:(UIImage *)image completion:(void (^)(NSInteger, NSError *))callback { // Get a pointer to the asset AVURLAsset* firstAsset = [AVURLAsset URLAssetWithURL:src options:nil]; // Make an instance of avmutablecomposition so that we can edit this asset: AVMutableComposition* mixComposition = [AVMutableComposition composition]; // Add tracks to this composition AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo prefernetworkingTrackID:kCMPersistentTrackID_Invalid]; // Audio track AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio prefernetworkingTrackID:kCMPersistentTrackID_Invalid]; // Image video is always 30 seconds. So we use that unless the background video is smaller. CMTime inTime = CMTimeMake( MAX_VIDEO_LENGTH * VIDEOFRAME, VIDEOFRAME ); if ( CMTimeCompare( firstAsset.duration, inTime ) < 0 ) { inTime = firstAsset.duration; } // Add to the video track. NSArray *videos = [firstAsset tracksWithMediaType:AVMediaTypeVideo]; CGAffineTransform transform; if ( videos.count > 0 ) { AVAssetTrack *track = [videos objectAtIndex:0]; [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, inTime) ofTrack:track atTime:kCMTimeZero error:nil]; transform = track.prefernetworkingTransform; videoTrack.prefernetworkingTransform = transform; } // Add the audio track. NSArray *audios = [firstAsset tracksWithMediaType:AVMediaTypeAudio]; if ( audios.count > 0 ) { [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, inTime) ofTrack:[audios objectAtIndex:0] atTime:kCMTimeZero error:nil]; } NSLog(@"Natural size: %.2f x %.2f", videoTrack.naturalSize.width, videoTrack.naturalSize.height); // Set the mix composition size. mixComposition.naturalSize = crop.size; // Set up the composition parameters. AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; videoComposition.frameDuration = CMTimeMake(1, VIDEOFRAME ); videoComposition.renderSize = crop.size; videoComposition.renderScale = 1.0; // Pass through parameters for animation. AVMutableVideoCompositionInstruction *passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; passThroughInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, inTime); // Layer instructions AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; // Set the transform to maintain orientation if ( scale != 1.0 ) { CGAffineTransform scaleTransform = CGAffineTransformMakeScale( scale, scale); CGAffineTransform translateTransform = CGAffineTransformTranslate( CGAffineTransformIdentity, -crop.origin.x, -crop.origin.y); transform = CGAffineTransformConcat( transform, scaleTransform ); transform = CGAffineTransformConcat( transform, translateTransform); } [passThroughLayer setTransform:transform atTime:kCMTimeZero]; passThroughInstruction.layerInstructions = @[ passThroughLayer ]; videoComposition.instructions = @[passThroughInstruction]; // If an image is given, then put that in the animation. if ( image != nil ) { // Layer that merges the video and image CALayer *parentLayer = [CALayer layer]; parentLayer.frame = CGRectMake( 0, 0, crop.size.width, crop.size.height); // Layer that renders the video. CALayer *videoLayer = [CALayer layer]; videoLayer.frame = CGRectMake(0, 0, crop.size.width, crop.size.height ); [parentLayer addSublayer:videoLayer]; // Layer that renders flyerly image. CALayer *imageLayer = [CALayer layer]; imageLayer.frame = CGRectMake(0, 0, crop.size.width, crop.size.height ); imageLayer.contents = (id)image.CGImage; [imageLayer setMasksToBounds:YES]; [parentLayer addSublayer:imageLayer]; // Setup the animation tool videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer]; } // Now export the movie AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality]; exportSession.videoComposition = videoComposition; // Export the URL exportSession.outputURL = dest; exportSession.outputFileType = AVFileTypeQuickTimeMovie; exportSession.shouldOptimizeForNetworkUse = YES; [exportSession exportAsynchronouslyWithCompletionHandler:^{ callback( exportSession.status, exportSession.error ); }]; } 

Estoy llamando a esta function desde AppDelegate.m

 - (void)applicationDidEnterBackground:(UIApplication *)application { bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{ // Clean up any unfinished task business by marking where you // stopped or ending the task outright. [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; // Start the long-running task and return immediately. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Do the work associated with the task, preferably in chunks. [self goingToBg]; [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }); NSLog(@"backgroundTimeRemaining: %f", [[UIApplication shanetworkingApplication] backgroundTimeRemaining]); } 

Hizo mucho RND en este problema, no encontré solución para ello.

¿Quiere compartir algunos enlaces? Espero que ayude a la comunidad de stack si están en el mismo problema [requisito].

Enlace1: AVExportSession para ejecutarse en segundo plano

Cita relacionada con la pregunta [copyda desde arriba Link1]

Lamentablemente, dado que AVAssetExportSession usa la gpu para hacer parte de su trabajo, no puede ejecutarse en segundo plano si está utilizando una AVVideoComposition.

Enlace2: inicio de AVAssetExportSession en segundo plano

Cita relacionada con la pregunta [copyda desde arriba Link2]

Puede iniciar AVAssetExportSession en segundo plano. Las únicas limitaciones en AVFoundation para realizar trabajo en segundo plano, están utilizando AVVideoCompositions o AVMutableVideoCompositions. AVVideoCompositions está utilizando la GPU, y la GPU no puede utilizarse en segundo plano

Url (s) para tareas de background:

APPLE DEV URL

URL de RAYWENDERLICH

Pregunta de stack

Demasiado tarde para la fiesta, si actualiza la configuration de "Modos de background" en las capacidades del proyecto para include Audio. Permitirá la export.

Esto está pensado para reproducir música en segundo plano.

Esto funciona para mi.