2015-06-25 86 views
0

我有一個相機應用程序,我試圖限制捕獲長度爲,正好是 15秒。NSTimer給出不準確的結果

我嘗試了兩種不同的方法,他們都不滿意我的工作。

第一種方法是火一個重複的計時器每秒:

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countTime:) userInfo:[NSDate date] repeats:YES]; 

- (void)countTime:(NSTimer*)sender { 
    NSDate *start = sender.userInfo; 
    NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:start]; 
    NSInteger time = round(duration); 
    if (time > 15) { 
     [self capture:nil]; // this stops capture 
    } 
} 

這給了我15秒的視頻8/10倍,具有周期性16秒一個...我已經嘗試了混合的NSTimeInterval雙鍵和圓形整數這裏,沒有明顯的差別...

的第二種方法是所希望的持續時間之後啓動一次選擇,像這樣:

self.timer = [NSTimer scheduledTimerWithTimeInterval:15.0f target:self selector:@selector(capture:) userInfo:nil repeats:NO]; 

這只是直接調用捕獲方法 - 即停止相機捕獲 - 並給出了相同的結果...

有沒有什麼,我在這裏忽略?現在,因爲我已經測試了一些經過調整的浮點值作爲上限(14.5,15.0,15.1,15.5,16.0等),而且幾乎總是在幾次嘗試後看到一個16秒的視頻,我我開始懷疑它是否只是AVFoundation需要第二次沖洗緩衝區...?

+1

不確定什麼*是*正確的,但它不是'NSTimer':http:// stackoverflow。com/questions/11835023/nstimer-accuracy – Linuxios

+1

您可能只需將視頻裁剪爲15秒後的字詞,並保持簡單的NSTimer。 – Linuxios

+0

@Linuxios是的,這可能是正確的選擇.... –

回答

2

的NSTimer是不能保證火的時候,你想讓它,你想讓它火剛過:

從蘋果的文檔:

計時器不是實時的機制;只有在添加了定時器的運行循環模式之一運行並且能夠檢查定時器的觸發時間是否已過時,它纔會觸發。由於典型的運行循環管理各種輸入源,所以定時器的時間間隔的有效分辨率被限制在50-100毫秒的量級上。如果在長時間標註期間發生定時器的觸發時間,或者運行循環處於未監視定時器的模式下,定時器將在下次運行循環檢查定時器時才觸發。因此,計時器觸發的實際時間可能是計劃的點火時間之後的相當長的一段時間。另請參閱定時器公差。

但是要回答你的問題,我曾經爲一家擁有最長15秒視頻的公司工作。我沒有寫視頻代碼,但我認爲我們在事實之後使用了AVComposition,以確保視頻不超過15秒。即使如此,它可能有時會縮短一幀。請參閱How do I use AVFoundation to crop a video

0

感謝Paul和Linuxious的評論和回答......以及Rory在盒子外面思考(有趣的選項)。

是的,最後很明顯,NSTimer本身並不足夠。

最後,我偵聽captureOutput委託方法來觸發,測試資產的長度,並適當修剪組合。

- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL 
     fromConnections:(NSArray *)connections error:(NSError *)error 
{ 
    _isRecording = NO; 

    AVURLAsset *videoAsset = [AVURLAsset assetWithURL:outputFileURL]; 
    CMTime length = [videoAsset duration]; 
    CMTimeShow(length); 

    if(CMTimeGetSeconds(length) > 15) 
    { 
     NSLog(@"Capture Longer Than 15 Seconds - Attempting to Trim"); 

     Float64 preferredDuration = 15; 
     int32_t preferredTimeScale = 30; 
     CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(preferredDuration, preferredTimeScale)); 

     AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPresetHighestQuality]; 
     exportSession.outputURL = outputFileURL; 
     exportSession.outputFileType = AVFileTypeQuickTimeMovie; 
     exportSession.timeRange = timeRange; 

     NSError *err = nil; 
     [[NSFileManager defaultManager] removeItemAtURL:outputFileURL error:&err]; 
     if (err) { 
      NSLog(@"Error deleting File: %@", [err localizedDescription]); 
     } 
     else { 
      [exportSession exportAsynchronouslyWithCompletionHandler:^{ 
       if (exportSession.status == AVAssetExportSessionStatusCompleted) { 
        NSLog(@"Export Completed - Passing URL to Delegate"); 
        if ([self.delegate respondsToSelector:@selector(didFinishRecordingToOutputFileAtURL:error:)]) { 
         [self.delegate didFinishRecordingToOutputFileAtURL:outputFileURL error:error]; 
        } 
       } 
       else if(exportSession.status == AVAssetExportSessionStatusFailed) { 
        NSLog(@"Export Error: %@", [exportSession.error localizedDescription]); 
        if ([self.delegate respondsToSelector:@selector(didFinishRecordingToOutputFileAtURL:error:)]) { 
         [self.delegate didFinishRecordingToOutputFileAtURL:outputFileURL error:exportSession.error ]; 
        } 
       } 
      }]; 
     } 

    } 

}