从AVAssetWriter读取实时H.264输出时,数据损坏(Data corruption wh

2019-07-03 12:00发布

我使用一些技巧来尝试当它被写入到磁盘中读取的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);
}

以下是我已经发现了一些类似的问题,但不完全回答我的问题:

  • 获取PTS从原料H264 MDAT通过iOS的AVAssetWriter产生
  • 流媒体视频从iPhone
  • 从的QuickTime MOV文件的解析H.264 NAL单元
  • 实时音频/视频流从iPhone到另一个设备(浏览器或iPhone)

当我终于想出解决办法,我会发布一个开放源码库,以协助人们谁试图做到这一点在未来。

谢谢!

更新:腐败不会在EOF边界发生。 这似乎是文件的部分被重新写入后finishWriting被调用。 第一个文件是在4KB分块,所以面积变化不是EOF边界附近的任何地方。 这似乎当要接近新的“MOOV”元素损坏以及movieFragmentInterval启用。

在右边的左边,破碎的文件正确的文件。

Answer 1:

我最终放弃,取而代之的是手动分块的方式,我称之为的“而它的所写”的做法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



Answer 2:

当读取一个文件MOV是iOS上的主动启动记录,则必须检查是否有改变提到的4个字节,并重新写这四个字节,然后在文件检查额外的数据,并发送额外的数据。 然后完成后,截断文件写入的文件大小。

显然,这取决于你要发送的文件。 我使用发送(偏移,字节数)到接收器。 所以,我发送“额外数据”,“更多的额外数据”,......,在(24,4)的新数据,“更多的额外数据”。

当文件是关于(又名后最后的媒体写)被关闭通常仅适用于iOS写记录的4字节(数据部分的大小)。 (见“的QuickTime原子”的信息)。 不幸的是,这也意味着,直至拍摄结束(在文件末尾写的和电影描述符)的MOV文件不能播放。



文章来源: Data corruption when reading realtime H.264 output from AVAssetWriter