读取AVAssetWriter的实时H.264输出时数据损坏 [英] Data corruption when reading realtime H.264 output from AVAssetWriter

查看:275
本文介绍了读取AVAssetWriter的实时H.264输出时数据损坏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一些技巧来尝试将AVAssetWriter的原始输出写入磁盘.当我通过串联重组单个文件时,生成的文件与AVAssetWriter的输出文件的字节数完全相同.但是,重新组合的文件将不会在QuickTime中播放,也不会被FFmpeg解析,因为存在数据损坏.此处和此处的几个字节已更改,导致生成的文件不可用.我认为这是在每次读取的EOF边界上发生的,但这不是一致的损坏.

I'm using some tricks to try to read the raw output of an AVAssetWriter while it is being written to disk. When I reassemble the individual files by concatenating them, the resulting file is the same exact number of bytes as the AVAssetWriter's output file. However, the reassembled file will not play in QuickTime or be parsed by FFmpeg because there is data corruption. A few bytes here and there have been changed, rendering the resulting file unusable. I assume this is occurring on the EOF boundary of each read, but it isn't consistent corruption.

我计划最终使用与此类似的代码来解析编码器中的各个H.264 NAL单元,以对它们进行打包并通过RTP发送,但是,如果我不相信从磁盘读取的数据,我可能不得不使用其他解决方案.

I plan to eventually use code similar to this to parse out individual H.264 NAL units from the encoder to packetize them and send them over RTP, however if I can't trust the data being read from disk I might have to use another solution.

是否有针对此数据损坏的解释/解决方法?您是否还有其他资源/链接可以找到如何解析NAL单元以通过RTP打包的其他资源/链接?

Is there an explanation/fix for this data corruption? And are there any other resources/links you have found on how to parse the NAL units to packetize over 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);
}

以下是我发现的一些类似问题,但并未完全回答我的问题:

Here are some similar questions I have found, but don't exactly answer my question:

  • Get PTS from raw H264 mdat generated by iOS AVAssetWriter
  • streaming video FROM an iPhone
  • Parsing h.264 NAL units from a quicktime MOV file
  • Realtime Audio/Video Streaming FROM iPhone to another device (Browser, or iPhone)

当我最终弄清楚这一点时,我将发布一个开源库,以帮助将来尝试这样做的人.

When I eventually figure this out, I will release an open source library to assist people who try to do this in the future.

谢谢!

更新:在EOF边界不会发生损坏.在调用finishWriting之后,似乎文件的某些部分已被重写.第一个文件的大小为4KB,因此更改的区域不在EOF边界附近.启用movieFragmentInterval时,它在新的"moov"元素附近也似乎已损坏.

Update: The corruption doesn't happen at the EOF boundary. It seems like parts of the file are re-written after finishWriting is called. This first file was chunked at 4KB, so the area changed isn't anywhere near an EOF boundary. It seems to be corrupted near new "moov" elements as well when movieFragmentInterval is enabled.

左侧是正确的文件,右侧是损坏的文件.

Correct file on the left, broken file on the right.

推荐答案

读取在iOS上正在积极录制的MOV文件时,您必须检查上述4个字节的更改,然后重新写入这4个字节,然后检查文件中的其他数据,然后发送其他数据.然后,完成后,将文件截断为写入的文件大小.

When reading a MOV file that is ACTIVELY recording on iOS, you MUST check the 4 bytes mentioned for changes, and re-write this four bytes, then check for additional data in file, and send additional data. Then when done, truncate the file to the file size written.

显然,这取决于您将文件发送到的位置.我使用发送(偏移量,字节数)到接收器.因此,我在(24,4)发送其他数据",更多附加数据",...,新数据,更多附加数据".

Obviously this depends on where you are sending the file. I use a send (offset,number of bytes) to receiver. So I send "additional data", "more additional data", ... , new data at (24,4), "more additional data".

通常,iOS仅在文件即将关闭时(也就是在最后一次写入媒体之后)才写入4字节(数据段的大小)记录. (请参阅有关"Quicktime原子"的信息).不幸的是,这也意味着在录制完成之前MOV文件是不可播放的(影片描述符写在文件的末尾).

Typically iOS only writes the 4 byte (size of data section) record when file is about to be closed (aka after last media write). (see info on "Quicktime atoms"). Unfortunately, this also means the MOV file is not PLAYABLE until recording is completed (and movie descriptors written at END of file).

这篇关于读取AVAssetWriter的实时H.264输出时数据损坏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆