ios - AVAssetExportSession deja de progresar
avfoundation (3)
¡He encontrado eso!
Necesitas establecer las instrucciones de capa correctas:
AVMutableCompositionTrack *track = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableVideoCompositionLayerInstruction * layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:track];
AVMutableVideoCompositionInstruction * MainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
MainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, allTime);
MainInstruction.layerInstructions = [NSArray arrayWithObject:layerInstruction];
De lo contrario simplemente se atascó.
Tengo un problema con AVAssetExportSession donde el progreso deja de aumentar pero el estado aún dice que se está exportando. Esto es realmente una ocurrencia bastante rara, funciona sin problemas aproximadamente el 99.99% del tiempo, pero quiero solucionar el problema de todos modos.
Así que empiezo la exportación:
exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];
exportSession.videoComposition = videoComposition;
exportSession.outputFileType = @"com.apple.quicktime-movie";
exportSession.outputURL = outputURL;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
...
}];
Entonces ten un temporizador que compruebe el progreso:
AVAssetExportSessionStatus status = [exportSession status];
float progress = 0;
if (status == AVAssetExportSessionStatusExporting) {
progress = [exportSession progress];
} else if (status == AVAssetExportSessionStatusCompleted) {
progress = 1;
}
NSLog(@"%d %f", status, progress);
[delegate processor:self didProgress:progress];
Y la salida termina pareciéndose a:
2012-05-23 14:28:59.494 **********[1899:707] 2 0.125991
2012-05-23 14:28:59.994 **********[1899:707] 2 0.185280
2012-05-23 14:29:00.494 **********[1899:707] 2 0.259393
2012-05-23 14:29:00.994 **********[1899:707] 2 0.326093
2012-05-23 14:29:01.494 **********[1899:707] 2 0.400206
2012-05-23 14:29:01.995 **********[1899:707] 2 0.481729
2012-05-23 14:29:02.495 **********[1899:707] 2 0.541019
2012-05-23 14:29:02.997 **********[1899:707] 2 0.622542
2012-05-23 14:29:03.493 **********[1899:707] 2 0.681832
2012-05-23 14:29:03.995 **********[1899:707] 2 0.763355
2012-05-23 14:29:04.494 **********[1899:707] 2 0.822645
2012-05-23 14:29:04.994 **********[1899:707] 2 0.880082
2012-05-23 14:29:05.493 **********[1899:707] 2 0.880082
2012-05-23 14:29:05.994 **********[1899:707] 2 0.880082
...
2012-05-23 14:43:22.994 **********[1899:707] 2 0.880082
2012-05-23 14:43:23.493 **********[1899:707] 2 0.880082
2012-05-23 14:43:23.994 **********[1899:707] 2 0.880082
2012-05-23 14:43:24.494 **********[1899:707] 2 0.880082
(Nota: no se detiene en el mismo porcentaje cada vez, es totalmente aleatorio)
Como puede ver en las marcas de tiempo, tardó 5 segundos en hacer el primer 88%, y luego lo dejé correr por otros 13 minutos (el procesamiento completo del video generalmente no toma más de 10 segundos) sin cambios en el progreso.
Actualmente, mi única opción es verificar si el progreso no ha cambiado en los últimos X segundos y solo decirle al usuario que falló y volver a intentarlo.
¿Alguien tiene alguna idea?
En mi caso, este problema provino de mp4s que fueron codificados con configuraciones inusuales. Pero en última instancia, un error en el código de Apple causa el problema.
Específicamente estaba trabajando con mp4s en Facebook. Parece que cuando Facebook codifica videos, cae o cambia el primer cuadro de la pista de video. El examen del video con ffprobe mostró:
"r_frame_rate": "722/25",
"avg_frame_rate": "722/25",
"time_base": "1/28880",
"start_pts": 1000,
"start_time": "0.034626",
0.034626 es exactamente el tiempo para el fotograma 2. La pista de audio, sin embargo, comenzó en 0.
Desafortunadamente, las API de Apple no informan correctamente esta hora de inicio para la pista. Después de obtener el AVAssetTrack del AVAsset y verificar su timeRange, informó que la hora de inicio era 0. Es posible que no esté entendiendo la naturaleza de la sincronización de la pista y los recursos, pero esto parece un error.
Por supuesto, al crear la composición de video para la sesión de exportación, utilicé el rango de tiempo de seguimiento que era incorrecto. Sospecho que cuando la sesión de exportación busca datos en la pista en el momento 0 y no encuentra nada, se confunde, y luego, como hemos visto, simplemente se cuelga y ni siquiera informa un error, otro error. Incluso crear la composición con videoCompositionWithPropertiesOfAsset: no ayuda.
Todavía no he intentado usar AVAssetReader y AVAssetWriter en lugar de AVAssetExportSession, así que no sé si se encontraría con la misma situación allí. Hasta que tenga más tiempo para explorar que he encontrado este truco como solución:
Restablezca el intervalo de tiempo utilizado para la inserción de pistas y las instrucciones de video para comenzar en 1 o 2 cuadros en:
CMTimeRange videoTimeRange = videoTrack.timeRange;
videoTimeRange.start = CMTimeMake(1, ceil(videoTrack.nominalFrameRate));
Obviamente, este truco solo se aplica a la situación particular en la que la hora de inicio de la pista de video no es 0. Supongo que, en general, este error se debe a las inusuales codificaciones de medios que AVAssetExportSession y sus clases relacionadas no esperan.
Me pregunto si es un problema con el enhebrado, si el temporizador y el exportador terminan en diferentes hilos. (La guía AVFoundation dice que no se garantiza que el exportador se ejecute en ningún subproceso en particular).
¿Ha intentado usar la observación de valores clave en lugar de un temporizador?
Ejemplo de Barebones:
// register for the notification
[exportSession addObserver:someProcessor forKeyPath:@"progress" options:NSKeyValueObservingOptionNew context:NULL];
Luego en su procesador:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
// add some checks here
NSNumber *processNumber = [change objectForKey:NSKeyValueChangeNewKey];
float process = [processNumber floatValue];
[delegate processor:self didProgress:progress];
}