AVAssetExportSession stops progressing
Asked Answered
R

4

17

I'm having a problem with AVAssetExportSession where the progress stops increasing but the status still says that it is exporting. This is actually a pretty rare occurrence, it works flawlessly about 99.99% of the time, but I want to fix the problem anyway.

So I start the exporting:

    exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];
    exportSession.videoComposition = videoComposition; 
    exportSession.outputFileType = @"com.apple.quicktime-movie";
    exportSession.outputURL = outputURL;
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        ...
    }];

Then have a timer checking the progress:

    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];

And the output ends up looking like:

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

(Note: It doesn't stop at the same percentage every time, its totally random)

As you can see from the timestamps it took 5 seconds to do the first 88%, and then I let it run for another 13 minutes (full video processing usually doesn't take more then 10 seconds) with no change in the progress.

Currently my only option is to check if the progress hasn't changed in the last X seconds and just tell the user it failed and to try again.

Anyone have any ideas?

Roma answered 23/5, 2012 at 20:43 Comment(12)
what kind of data are you trying to write onto the export session it may be useful to knowIndiscretion
Adding a video onto the end of another and replacing part of the audio with another part of an audio file. Using the videoComposition for setting the preferred transforms of the videos at their start times.Roma
Having the same issue too. any progress on this?Pileate
Same here - I'm using videoComposition too.Eastward
Anyone have any luck? I'm doing the same, and having to resort to the same hack - checking using an NSTimer and if it doesn't complete within a certain time, cancelling and retrying. Very flaky though.Courtney
I am also seeing a similar issue. Although my code works flawlessly most of the time. Less than 1% of users are seeing this issue where it just sits at a % without failing or progressing. Interesting thing is I cannot replicate it on any of our devices or simulators.Gerius
Same issue here, with one exception - I getting such stuck EVERY time. The only way to fix at my side - use AVAssetExportPresetPassthrough option. Google doesn't says something valuable...Andresandresen
@Andresandresen Will you test out my solution below?Peachey
I am seeing the same problem only on IOS7. Works fine on IOS6Nicolenicolea
I am experiencing this issue too with mp4s. It seems to depend upon the video file being processed. Some mp4s work flawlessly, others hang with no callback every time. Also worth noting - if I export without a video composition it works, but as soon as I add a composition, even if it is created directly form the asset with videoCompositionWithPropertiesOfAsset and not modified, the export hangs. Inspecting the status of the export session shows AVAssetExportSessionStatusExportingSerrulate
my situation is exactly same, [AVAssetExportSession progress] stuck at 0.0 sometime, but sometime it's fine.Missive
@Serrulate Did any of you get anywhere with this? I'm also experiencing the hanging with mp4's (only when using a composition). The strange thing is, I've created all my mp4's myself in my own app, using AVAssetWriter. If there's a problem with the file, as some of the below answers suggests, I might be able to fix it, however, I can't see what I'm doing wrong. If any of you found out anything, or another work-around, that'd be appreciated!Copeck
S
4

In my case this problem stemmed from mp4s that were encoded with unusual configurations. But ultimately a bug in Apple's code causes the issue.

Specifically I was working with mp4s on Facebook. It seems that when facebook encodes videos it drops or shifts the first frame of the video track. Examining the video with ffprobe showed:

        "r_frame_rate": "722/25",
        "avg_frame_rate": "722/25",
        "time_base": "1/28880",
        "start_pts": 1000,
        "start_time": "0.034626",

0.034626 is exactly the time for frame 2. The audio track, however, started at 0.

Unfortunately Apple's APIs don't correctly report this start time for the track. After getting the AVAssetTrack from the AVAsset and checking its timeRange it reported the start time as 0. It's possible I'm misunderstanding the nature of track and asset timing, but this seems like a bug.

Of course, when building the video composition for the export session I used the track time range which was incorrect. I suspect that when the export session looks for data in the track at time 0 and finds nothing it gets confused, and then as we've seen, just hangs and doesn't even report an error - another bug. Even creating the composition with videoCompositionWithPropertiesOfAsset: doesn't help.

I have not yet tried to use AVAssetReader and AVAssetWriter instead of AVAssetExportSession so I don't know if you would encounter the same situation there. Until I have more time to explore that I've come up with this hack as a solution:

Reset the time range used for track insertion and video instructions to start at 1 or 2 frames in:

CMTimeRange videoTimeRange = videoTrack.timeRange;
videoTimeRange.start = CMTimeMake(1, ceil(videoTrack.nominalFrameRate));

Obviously this hack only applies to the particular situation where the video track start time is not 0. I would guess that in general this bug arises from unusual media encodings that AVAssetExportSession and its related classes are not expecting.

Serrulate answered 10/2, 2014 at 3:29 Comment(5)
Did you ever find a better solution for this? I tried doing this, but I'm not sure where you meant to change the starting position. When adding my assets to the track, I'm using [track insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)...asset..] When trying to use your CMTimeRange-method, it's not working. Actually, it gets worse, as I later can't retrieve the naturalSize of the tracks for some reason. But I might be putting the frame at the wrong place? Anyway, a proper solution to this error would be a lot better.Copeck
My work on this stopped before I found a better solution. There's no guarantee this will work in your case, and in general there are many pitfalls when working with AVFoundation. But you would use the lines above to create an adjusted range to use for track insertion. For example: [track insertTimeRange:adjustedVideoTimeRange ofTrack:videoTrack atTime:kCMTimeZero error:&error]Serrulate
As I wrote, I tried that with no luck. It's still happening. The weird thing is that all video-files I'm using is created in the app itself, using AVAssetWriter. I'm not sure if I'm experiencing the same problem. I'm now trying to use/run ffprobe on one of the failed files, but I don't know how to use it. When running (on osx/terminal) ./ffprobe test.mp4 I only see some of the metadata about the file. The documentation is weirdly weird, is there an option I should be using to get your result?Copeck
I now used ./ffprobe -show_streams test.mp4 and was presented with two [STREAM]..[/STREAM]s. The first one was for audio (aac for codec and Core Media Audio for TAG:handler_name), it had a start_time at 0.010295. While the second stream (codec h264 and Core Media Video) had start_time set to 0.000000. This is the opposite of what you had, so maybe this isn't the same problem.. However, I'm seeing strange variables otherwise for the video-stream: r_frame_rate=30000/1001, avg_frame_rate = 48900/1631, time_base = 1/600 and start_pts=0. Do you know what these numbers are?Copeck
Sounds like you're experiencing different issues. You should search SO some more and post a new question if you don't find anything. Good luck!Serrulate
A
3

I've found that!

You need to set correct layerInstructions:

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];

Otherwise it just stuck.

Andresandresen answered 17/2, 2013 at 20:47 Comment(1)
This is exactly what it was for me. My export was getting stuck at 0.005 (basically nothing) and the callback was never firing. Thanks!Griz
P
2

I'm wondering if it's an issue with threading - if the timer and the exporter end up in different threads. (The AVFoundation guide says that the exporter is not guaranteed to run on any particular thread.)

Have you tried using key-value observing instead of a timer?

Barebones example:

// register for the notification
[exportSession addObserver:someProcessor forKeyPath:@"progress" options:NSKeyValueObservingOptionNew context:NULL];

Then in your processor:

- (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];
}
Peachey answered 23/2, 2013 at 5:46 Comment(2)
This approach should not be used, as it is stated in the documentation that "progress" property is not key-value observable.Fingerboard
@PayalManiyar, sorry it was a long time ago and I really don't remember how I have sold this problem.Fingerboard
V
0

I had the same issue, and none of the other solutions (like setting layerInstructions) helped me.

I rearranged how my DispatchQueues work, and it appears to have fixed the problem. Specifically, AVAssetExportSessions were being created on multiple different (concurrent) DispatchQueues with the same AVAsset. The fix was to have the queue that created the AVAsset also (always) be the queue that creates the AVAssetExportSessions. I found that calling AVAssetExportSession.exportAsynchronously() from the global DispatchQueue is fine -- and necessary, if you want to have a timer that eventually times out should a failure occur.

Volturno answered 19/9, 2019 at 20:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.