1

我试图将一个iPhone上的实时视频缓冲区传输到另一个iPhone(称为客户端iPhone)进行预览显示,并且还接受来自客户端iPhone的命令。我正在想办法达到这个标准。我发现的最接近的东西是AVCaptureMultipeerVideoDataOutput,在GithubiOS中的点对点视频

但是仍然使用Multipeer连接框架,我认为它仍然需要在iPhone上进行一些设置。我想要的是应该理想的是在两个iPhone上都不需要设置,只要在两个iPhone上都启用了Wifi(或者如果可能的话,蓝牙),那么对等应该在应用内相互识别并提示用户关于设备发现。什么是实现这个标准方法以及指向示例代码的任何链接?

编辑:我从零开始编写代码后,通过Multipeer连接工作。截至目前,我正在通过缩减&将像素缓冲区发送到对等设备,将数据压缩为jpeg。在远程设备上,我在每帧时间显示数据的地方都安装了UIImage。不过,我认为UIKit可能不是显示数据的最佳方式,即使图像很小。如何使用OpenGLES显示这些数据?是否可以在Opengles中直接解码jpeg?

+0

AFAIK多路连接器具有满足您对发现和流式直播会话要求的所有API。如果你不想使用它,其他选项将使用AirPlay,但它是一种解决方法,并且无法帮助发现客户端附近的iPhone – Bluewings

+0

我尝试了MC的示例代码,但无法配对设备并发送数据。我需要的是一个简单的界面,其中一个iPhone抛出一个弹出窗口,一旦发现它就选择对等点。选择对等点启动数据流。我无法通过任何可用的示例代码使其工作,其中包括Apple on MC群聊中的代码。 –

回答

1

评论:

截至目前,我通过 缩减&发送所述像素缓冲器等设备为JPEG压缩数据。在远程设备上,I 具有UIImage设置,我在每个帧时间显示数据。然而 我认为UIKit可能不是显示数据的最佳方式,尽管 图像很小。

原来,这是通过Multipeer Connectivity框架传输图像的最佳方式。我试过所有的替代品:

  1. 我使用VideoToolbox压缩帧。太慢了。
  2. 我使用压缩压缩帧。太慢,但更好。

让我提供#2中的一些代码:

在iOS装置发送图像数据:

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection 
{ 
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    CVPixelBufferLockBaseAddress(imageBuffer,0); 
    __block uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer); 
    dispatch_async(self.compressionQueue, ^{ 
     uint8_t *compressed = malloc(sizeof(uint8_t) * 1228808); 
     size_t compressedSize = compression_encode_buffer(compressed, 1228808, baseAddress, 1228808, NULL, COMPRESSION_ZLIB); 
     NSData *data = [NSData dataWithBytes:compressed length:compressedSize]; 
     NSLog(@"Sending size: %lu", [data length]); 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      __autoreleasing NSError *err; 
      [((ViewController *)self.parentViewController).session sendData:data toPeers:((ViewController *)self.parentViewController).session.connectedPeers withMode:MCSessionSendDataReliable error:&err]; 
     }); 
    }); 
    CVPixelBufferUnlockBaseAddress(imageBuffer, 0); 
} 

在iOS装置显示图像数据:

typedef struct { 
    size_t length; 
    void *data; 
} ImageCacheDataStruct; 

- (void)session:(nonnull MCSession *)session didReceiveData:(nonnull NSData *)data fromPeer:(nonnull MCPeerID *)peerID 
{ 
    NSLog(@"Receiving size: %lu", [data length]); 
    uint8_t *original = malloc(sizeof(uint8_t) * 1228808); 
    size_t originalSize = compression_decode_buffer(original, 1228808, [data bytes], [data length], NULL, COMPRESSION_ZLIB); 

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef newContext = CGBitmapContextCreate(original, 640, 480, 8, 2560, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    CGImageRef newImage = CGBitmapContextCreateImage(newContext); 

    UIImage *image = [[UIImage alloc] initWithCGImage:newImage scale:1 orientation:UIImageOrientationUp]; 

    CGContextRelease(newContext); 
    CGColorSpaceRelease(colorSpace); 
    CGImageRelease(newImage); 

    if (image) { 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [((ViewerViewController *)self.childViewControllers.lastObject).view.layer setContents:(__bridge id)image.CGImage]; 
     }); 
    } 
} 

虽然此代码在接收端生成原始质量的图像,您会发现实时播放速度太慢。

下面是做到这一点的最好办法:

在iOS设备发送图像数据:

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection 
{ 
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 

    CVPixelBufferLockBaseAddress(imageBuffer,0); 
    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer); 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    CGImageRef newImage = CGBitmapContextCreateImage(newContext); 


    UIImage *image = [[UIImage alloc] initWithCGImage:newImage scale:1 orientation:UIImageOrientationUp]; 
    CGImageRelease(newImage); 
    CGContextRelease(newContext); 
    CGColorSpaceRelease(colorSpace); 
    CVPixelBufferUnlockBaseAddress(imageBuffer, 0); 

    if (image) { 
     NSData *data = UIImageJPEGRepresentation(image, 0.7); 
     NSError *err; 
     [((ViewController *)self.parentViewController).session sendData:data toPeers:((ViewController *)self.parentViewController).session.connectedPeers withMode:MCSessionSendDataReliable error:&err]; 
    } 
} 

在iOS设备上接收图像数据:

- (void)session:(nonnull MCSession *)session didReceiveData:(nonnull NSData *)data fromPeer:(nonnull MCPeerID *)peerID 
{ 
    dispatch_async(self.imageCacheDataQueue, ^{ 
     dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); 
     const void *dataBuffer = [data bytes]; 
     size_t dataLength = [data length]; 
     ImageCacheDataStruct *imageCacheDataStruct = calloc(1, sizeof(imageCacheDataStruct)); 
     imageCacheDataStruct->data = (void*)dataBuffer; 
     imageCacheDataStruct->length = dataLength; 

     __block const void * kMyKey; 
     dispatch_queue_set_specific(self.imageDisplayQueue, &kMyKey, (void *)imageCacheDataStruct, NULL); 

     dispatch_sync(self.imageDisplayQueue, ^{ 
      ImageCacheDataStruct *imageCacheDataStruct = calloc(1, sizeof(imageCacheDataStruct)); 
      imageCacheDataStruct = dispatch_queue_get_specific(self.imageDisplayQueue, &kMyKey); 
      const void *dataBytes = imageCacheDataStruct->data; 
      size_t length = imageCacheDataStruct->length; 
      NSData *imageData = [NSData dataWithBytes:dataBytes length:length]; 
      UIImage *image = [UIImage imageWithData:imageData]; 
      if (image) { 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        [((ViewerViewController *)self.childViewControllers.lastObject).view.layer setContents:(__bridge id)image.CGImage]; 
        dispatch_semaphore_signal(self.semaphore); 
       }); 
      } 
     }); 
    }); 
} 

原因信号量和单独的GCD队列很简单:您希望帧以相同的时间间隔显示。否则,视频开始有时会放慢速度,在加速超过正常速度以赶上之前。我的方案确保每个帧都以相同的速度连续播放,而不管网络带宽瓶颈。