重用时AVAssetWriter因AVAssetWriterStatusFailed失败

时间:2016-12-19 10:55:44

标签: c++ ios objective-c

我正在尝试使用AVAssetWriter对视频进行编码。我正在使用ARC。我想要做的是通过套接字流式传输视频。

现在问题是。我第一次使用的设置。 我无法再次使用AVAssetWriter,而无需先重新启动我的应用程序。如果我没有重新启动我的应用程序,当我调用[m_writer startWriting]时,我收到以下错误:

m_inputFile = [NSFileHandle fileHandleForReadingAtPath: m_writer.path];
m_readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [m_inputFile fileDescriptor], 0, m_readQueue);
dispatch_source_set_event_handler(m_readSource, ^{
  // Read bytes from file and send over socket
}
dispatch_source_set_cancel_handler(m_readSource, ^{
  [self cleanup];
}
dispatch_resume(m_readSource);
  • 我确保在调用cleaup方法时删除了文件。 我使用iExplorer导航到文件所在的临时文件夹 被创造。我还确定了AVAssetWriter的状态 在调用finshWriting之后是AVAssetWriterStatusFinished。

编辑:即使我使用了不同的文件名,错误仍然存​​在。

我创建了一个VideoEncoder类的实例(下面的代码),然后我将其提供原始图像。然后我等待AVAssetWriter使用dispatch_source_set_event_handler()将某些内容写入输出文件,即:

@implementation VideoEncoder : NSObject
{
  AVAssetWriter* m_writer;
  AVAssetWriterInput* m_writerInput;
  AVAssetWriterInputPixelBufferAdaptor* m_pixelBufferAdaptor;
}
-(id) initWithPath:(NSString*)path  width:(int)width height:(int)height parameters:(NSDictionary*) parameters
{
  if (self = [super init])
  {
    self.path = path;
    [[NSFileManager defaultManager] removeItemAtPath:self.path error:nil];
    NSURL* url = [NSURL fileURLWithPath: self.path];

    m_writer = [[AVAssetWriter alloc] initWithURL:url fileType:AVFileTypeQuickTimeMovie error: nil];

    NSDictionary* settings = [NSDictionary dictionaryWithObjectsAndKeys:
                          AVVideoCodecH264, AVVideoCodecKey,
                          [NSNumber numberWithInt: width], AVVideoWidthKey,
                          [NSNumber numberWithInt:height], AVVideoHeightKey,
                          parameters,                      AVVideoCompressionPropertiesKey,
                          nil];
    m_writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:settings];
    m_writerInput.expectsMediaDataInRealTime = YES;

    [m_writer addInput:m_writerInput];

    NSDictionary* pixelBufferOptions = [[NSDictionary alloc] initWithObjectsAndKeys:
                                        [NSNumber numberWithInt: kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey,
                                        [NSNumber numberWithInt: width*4],                   kCVPixelBufferBytesPerRowAllignmentKey,
                                        [NSNumber numberWithInt; width],                     kCVPixelBufferWidthKey,
                                        [NSNumber numberWithInt; height],                     kCVPixelBufferHeightKey,
                                        nil];

    m_pixelBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc]
                            initWithAssetWriterInput: m_writerInput
                            sourcePixelBufferAttributes: pixelBufferOptions];

    [m_writer startWriting];
    [m_writer startSessionAtSourceTime: kCMTimeZero];
  }
}

我为AVAssetWriter创建了一个名为VideoEncoder的包装器。以下是我初始化AVAssetWriter的方法:

-(BOOL) encodeFrame:(CVPixelBufferRead)pixelBuffer time(CMTime)time;
{
  if (m_writer.status == AVAssetWriterStatusFailed)
  {
    return NO;
  }
  if (m_writerInput.readyForMoreMediaData == YES)
  {
    if ([m_pixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:time])
    {
      return YES:
    }
  }
  return NO;
}

这是我给原始图像提供的方式:

-(bool) encodeImage:(const char*) frameBuffer width:(int)width height:(int)height
{
  CVPixelBufferRef pixelBuffer = NULL;
  CVReturn ret;

  ret = CVPixelBufferCraeteWithBytes( 
           kCFAllocatorDefault, 
           with, 
           height,
           kCVPixelFormatType_32BGRA,
           (void*) frameBuffer,
           width * 4,
           NULL, NULL, NULL, NULL, &pixelBuffer);
  if (ret == kCVReturnSuccess && pixelBuffer)
  {
    CFTimeInterval currTime = CACurrentMediaTime();
    if (m_frameIndex > 0 )
    {
      int ticks = (int)((currTime - m_prevTime) * 1000) / 30;
      CMTime addTime;

      ticks = ticks <= 0 ? 1 : ticks;
      addTime = CMTimeMake(ticks, 30);
      m_timestamp = CMTimeAdd(m_timestamp, addTime);
    }
    m_prevTime = currTime;
    [m_impl encodeFrame: pixelBuffer time: m_timestamp];

    CVBufferRelease(pixelBuffer);
    m_frameIndex ++;
  }
}

这是像素缓冲区的生成方式:

-(void) finishWritingWithCompletionHandler(void (^)(void))handler
{
  [m_writerInput markAsFinished];
  [m_writer finishWithCompletionHandler: ^{
    handler();
  }
}

这是我终止AVAssetWriter的方法:

-(void) makeFilename
{
  NSString* filename = [NSString* stringWithFormat: @"temp%d.tmp", 1];
  NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent: filename];
  return path;
}

bool CFrameEncoder::StartEncoding()
{
  m_impl = CFRetain((__bridge*void)[[VideoEncoder alloc] initWithPath:[self makeFilepath] width:800 height:480 parameters: params]);
}

以下是我使用包装器的方法。我使用这个包装器形成一个C ++类。实例:

bool CFrameEncoder::StopDecoding()
{
  [(__bridge VideoEncoder*) m_impl terminate];
  CFRelease(m_impl);
}

的dealloc:

-(void) terminate
{
  if (m_readSource)
  {
    dispatch_source_cancel(m_readSource);
  }
}
// The dispatch source cancel handler
-(void) cleanup
{
  m_readQueue = nil;
  m_readSource = nil;

  [m_inputFile closeFile];

  NSString* path = m_writer.path;
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

  [m_writer finishWithCompletionHandler:^{
    dispatch_semaphore_signal(semaphore);
  }];

  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  m_writer = nil;
  [[NSFileManager defaultManager] removeItemAtPath: path error: nil];
}

终止函数(VideoEncoder类):

{{1}}

1 个答案:

答案 0 :(得分:1)

AVAssetWriter类只能有有限数量的实例。如果您的应用程序超过此限制,那么当您尝试startWriting时,AVAssetWriter会因AVAssetWriterStatusFailed而失败。

这意味着我的代码中存在阻止AVAssetWriter被释放的内容。有谁知道在哪里?