4

我正在使用一些技巧嘗試在寫入磁盤時讀取AVAssetWriter的原始輸出。當我通過連接它們重組單個文件時,生成的文件與AVAssetWriter的輸出文件具有相同的字節數。但是,重新組合後的文件不會在QuickTime中播放,也不會由FFmpeg解析,因爲數據損壞。這裏和那裏的幾個字節已經更改,導致生成的文件不可用。我認爲這是發生在每次讀取的EOF邊界上,但它不是一致的腐敗。當從AVAssetWriter讀取實時H.264輸出時數據損壞

我打算最終使用類似於此的代碼來解析出編碼器中的各個H.264 NAL單元,以便將它們打包並通過RTP發送它們,但是如果我不能相信從磁盤讀取的數據,我可能會使用另一種解決方案。

有沒有解釋/修復這個數據損壞?還有沒有其他的資源/鏈接,你已經找到了如何解析NAL單元打包RTP?

全部代碼在這裏:AVAppleEncoder.m

// Modified from 
// http://www.davidhamrick.com/2011/10/13/Monitoring-Files-With-GCD-Being-Edited-With-A-Text-Editor.html 
- (void)watchOutputFileHandle 
{ 
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    int fildes = open([[movieURL path] UTF8String], O_EVTONLY); 

    source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,fildes, 
                   DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE, 
                   queue); 
    dispatch_source_set_event_handler(source,^
             { 
              unsigned long flags = dispatch_source_get_data(source); 
              if(flags & DISPATCH_VNODE_DELETE) 
              { 
               dispatch_source_cancel(source); 
               //[blockSelf watchStyleSheet:path]; 
              } 
              if(flags & DISPATCH_VNODE_EXTEND) 
              { 
               //NSLog(@"File size changed"); 
               NSError *error = nil; 
               NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:movieURL error:&error]; 
               if (error) { 
                [self showError:error]; 
               } 
               [fileHandle seekToFileOffset:fileOffset]; 
               NSData *newData = [fileHandle readDataToEndOfFile]; 
               if ([newData length] > 0) { 
                NSLog(@"newData (%lld): %d bytes", fileOffset, [newData length]); 
                NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
                NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; 
                NSString *movieName = [NSString stringWithFormat:@"%d.%lld.%d.mp4", fileNumber, fileOffset, [newData length]]; 
                NSString *path = [NSString stringWithFormat:@"%@/%@", basePath, movieName]; 
                [newData writeToFile:path atomically:NO]; 
                fileNumber++; 
                fileOffset = [fileHandle offsetInFile]; 
               } 
              } 
             }); 
    dispatch_source_set_cancel_handler(source, ^(void) 
             { 
              close(fildes); 
             }); 
    dispatch_resume(source); 
} 

這裏是我已經發現了一些類似的問題,但不完全回答我的問題:

當我終於明白這一點時,我將發佈一個開源庫,以幫助未來嘗試這樣做的人。

謝謝!

更新:腐敗不會發生在EOF邊界。在調用finishWriting後,似乎文件的某些部分被重寫。這個第一個文件被分爲4KB,所以改變的區域不在EOF邊界附近。在啓用movieFragmentInterval時,它似乎在新的「moov」元素附近被破壞。

broken file 正確的文件在左邊,破碎的文件在右邊。

+0

不確定你在這裏試圖做什麼,你只是試圖回放文件assetWriter正在創建緩存,我認爲有更簡單的方法來做到這一點。 –

+0

不,我正在計劃解析文件並提取各個H.264 NAL單元進行打包並通過RTP發送,或者將正在寫入的部分文件發送到服務器上進行連接。 –

回答

2

我最終放棄了「在寫入時閱讀」的方法,贊成手動分塊方法,我在後臺線程上每5秒鐘調用一次finishWriting。我能使用的方法originally described here丟棄幀的可以忽略不計數量:

- (void) segmentRecording:(NSTimer*)timer { 
    AVAssetWriter *tempAssetWriter = self.assetWriter; 
    AVAssetWriterInput *tempAudioEncoder = self.audioEncoder; 
    AVAssetWriterInput *tempVideoEncoder = self.videoEncoder; 
    self.assetWriter = queuedAssetWriter; 
    self.audioEncoder = queuedAudioEncoder; 
    self.videoEncoder = queuedVideoEncoder; 
    //NSLog(@"Switching encoders"); 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
     [tempAudioEncoder markAsFinished]; 
     [tempVideoEncoder markAsFinished]; 
     if (tempAssetWriter.status == AVAssetWriterStatusWriting) { 
      if(![tempAssetWriter finishWriting]) { 
       [self showError:[tempAssetWriter error]]; 
      } 
     } 
     if (self.readyToRecordAudio && self.readyToRecordVideo) { 
      NSError *error = nil; 
      self.queuedAssetWriter = [[AVAssetWriter alloc] initWithURL:[self newMovieURL] fileType:(NSString *)kUTTypeMPEG4 error:&error]; 
      if (error) { 
       [self showError:error]; 
      } 
      self.queuedVideoEncoder = [self setupVideoEncoderWithAssetWriter:self.queuedAssetWriter formatDescription:videoFormatDescription bitsPerSecond:videoBPS]; 
      self.queuedAudioEncoder = [self setupAudioEncoderWithAssetWriter:self.queuedAssetWriter formatDescription:audioFormatDescription bitsPerSecond:audioBPS]; 
      //NSLog(@"Encoder switch finished"); 

     } 
    }); 
} 

完整的源代碼:https://github.com/chrisballinger/FFmpeg-iOS-Encoder/blob/master/AVSegmentingAppleEncoder.m

+0

我正在處理類似問題,並使用一對AVAssetWriters來管理此問題。我發現個別MP4中的音頻和視頻軌道具有不同的長度,這在幾個級聯之後導致同步的主要問題。你有沒有設法解決這個問題? –

+0

謝謝,但我如何將相機輸入連接到AVAssetWriter? – sudo

2

當讀取一個MOV文件,該文件是在iOS主動啓動記錄,必須檢查所提到的4個字節進行更改並重新寫入這四個字節,然後檢查文件中的其他數據併發送其他數據。然後完成後,將文件截斷爲寫入的文件大小。

顯然這取決於您發送文件的位置。我使用發送(偏移量,字節數)到接收器。因此,我發送了「附加數據」,「更多附加數據」,...,(24,4)的新數據,「更多附加數據」。

當文件將要關閉時(也就是在最後一次寫入媒體後),通常iOS只寫入4字節(數據段的大小)記錄。 (參見關於「Quicktime原子」的信息)。不幸的是,這也意味着MOV文件不能播放直到錄製完成(並且電影描述符寫在文件的END處)。