5

ディスクへの書き込み中に AVAssetWriter の生の出力を読み取るために、いくつかのトリックを使用しています。個々のファイルを連結して再構築すると、結果のファイルは AVAssetWriter の出力ファイルとまったく同じバイト数になります。ただし、再構築されたファイルは、データが破損しているため、QuickTime で再生したり、FFmpeg で解析したりできません。あちこちで数バイトが変更されているため、結果のファイルは使用できなくなります。これは各読み取りの EOF 境界で発生していると思いますが、一貫した破損ではありません。

最終的にはこれに似たコードを使用して、エンコーダーから個々の 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」要素の近くでも破損しているようです。

壊れたファイル 左が正しいファイル、右が壊れたファイル。

4

2 に答える 2

2

結局、「書き込み中に読み取る」アプローチを放棄してfinishWriting、バックグラウンドスレッドで5秒ごとに呼び出す手動チャンクアプローチを採用しました。ここで最初に説明した方法を使用して、ごくわずかな数のフレームをドロップすることができました。

- (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

于 2012-11-02T01:18:30.680 に答える
2

iOS でアクティブに記録している MOV ファイルを読み取るときは、言及されている 4 バイトの変更を確認し、この 4 バイトを書き直してから、ファイル内の追加データを確認し、追加データを送信する必要があります。完了したら、書き込まれたファイル サイズに合わせてファイルを切り捨てます。

明らかに、これはファイルの送信先によって異なります。受信者への送信(オフセット、バイト数)を使用します。そこで、「追加データ」、「追加データ」、...、(24,4) の新しいデータ、「追加データ」を送信します。

通常、iOS は、ファイルが閉じられようとしているとき (最後のメディア書き込み後) にのみ 4 バイト (データ セクションのサイズ) のレコードを書き込みます。(「Quicktime atom」に関する情報を参照してください)。残念ながら、これはまた、MOV ファイルは、記録が完了するまで (およびファイルの END にムービー記述子が書き込まれるまで) 再生できないことを意味します。

于 2012-11-17T20:36:02.563 に答える