IOS从网络接收视频

时间:2012-04-07 16:59:32

标签: objective-c ios networking avfoundation bonjour

更新的 - 我修复了下面代码中的一些错误,图像显示在另一台设备上,但我还有另外一个问题。当视频捕获打开时,“主”设备连续发送数据,有时此捕获出现在“从”设备上,并且在很短的时间内,图像“闪烁”为空白并在短时间内一直重复此操作。有什么想法吗?

我正在开发一款需要将实时摄像头捕捉和现场麦克风捕捉功能发送到网络中其他设备的应用。

我已经使用TCP服务器完成了设备之间的连接,并将其与bonjour一起发布,这就像魅力一样。

最重要的部分是从“主”设备发送和接收视频和音频,并在“从”设备上呈现。

首先,这里是一段代码,其中app获取相机样本缓冲区并在UIImage中进行转换:

@implementation AVCaptureManager (AVCaptureVideoDataOutputSampleBufferDelegate)

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
  dispatch_sync(dispatch_get_main_queue(), ^{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
    NSData *data = UIImageJPEGRepresentation(image, 0.2);

    [self.delegate didReceivedImage:image];
    [self.delegate didReceivedFrame:data];

    [pool drain];
  });
}


- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer
{
  CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

  CVPixelBufferLockBaseAddress(imageBuffer, 0);

  size_t width = CVPixelBufferGetWidth(imageBuffer);
  size_t height = CVPixelBufferGetHeight(imageBuffer);
  size_t bytesPerRow = width * 4;

  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

  void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);

  CGContextRef context = CGBitmapContextCreate(
                                               baseAddress,
                                               width,
                                               height,
                                               8,
                                               bytesPerRow,
                                               colorSpace,
                                               kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little
                                               );

  CGImageRef quartzImage = CGBitmapContextCreateImage(context);

  UIImage *image = [UIImage imageWithCGImage:quartzImage];
  CGImageRelease(quartzImage);
  CGColorSpaceRelease(colorSpace);
  CGContextRelease(context);
  CVPixelBufferUnlockBaseAddress(imageBuffer, 0);

  return image;
}

@end

消息“[self.delegate didReceivedImage:image];”只是为了测试主设备上的图像捕获,这个图像适用于捕获设备。

接下来是关于如何将其发送到网络:

- (void) sendData:(NSData *)data
{
  if(_outputStream && [_outputStream hasSpaceAvailable])
  {
    NSInteger bytesWritten = [_outputStream write:[data bytes] maxLength:[data length]];

    if(bytesWritten < 0)
      NSLog(@"[ APP ] Failed to write message");

  }
}

看我正在使用RunLoop来编写和读取流,我认为这比打开更好,并且不断关闭流。

接下来,我在从设备上收到“NSStreamEventHasBytesAvailable”事件,这段代码处理这个:

case NSStreamEventHasBytesAvailable:
      /*I can't to start a case without a expression, why not?*/
      NSLog(@"[ APP ] stream handleEvent NSStreamEventHasBytesAvailable");
      NSUInteger bytesRead;
      uint8_t buffer[BUFFER_SIZE];

      while ([_inputStream hasBytesAvailable])
      {
        bytesRead = [_inputStream read:buffer maxLength:BUFFER_SIZE];
        NSLog(@"[ APP ] bytes read:  %i", bytesRead);

        if(bytesRead)
          [data appendBytes:(const void *)buffer length:sizeof(buffer)];
      }


      [_client writeImageWithData:data];

      break;

BUFFER_SIZE的值是32768。 我认为while块不是必需的,但我使用它是因为如果我在第一次迭代时无法读取所有可用字节,我可以在下一个读取。

所以,这就是重点,流正确但NSData上序列化的图像似乎已损坏,接下来,我只是将数据发送给客户端......

[_client writeImageWithData:data];

...并在客户端类中创建一个简单的UIImage,就像这样...

[camPreview setImage:[UIImage imageWithData:data]];

在camPreview中(是的是UIImageView),我有一个图像只是为了在屏幕上显示占位符,当我从网络获取图像并传递给camPreview时,占位符变为空白。

其他想法是关于输出,当我开始捕获时,我收到数据的第一部分,我从系统收到此消息:

  

错误:ImageIO:JPEG损坏的JPEG数据:标记0xbf

之前的28个外部字节      

错误:ImageIO:JPEG不支持的标记类型0xbf

经过一段时间后,我再收到这条消息了。

重点是找到“奴隶”设备上没有显示图像的原因。

感谢。

2 个答案:

答案 0 :(得分:0)

我不确定您发送图像的频率,但即使不常见,我认为我会扫描JPEG数据中的SOI和EOI标记,以确保您拥有所有数据。这是我很快找到的post

答案 1 :(得分:0)

我在渲染之前找到了answer来检查jpeg格式。

这解决了我的问题,现在我可以显示从“主”ios设备到“奴隶”ios设备的视频捕获。