1

Я пытаюсь передавать видеопотоки в реальном времени на одном iPhone на другой iPhone (называемый клиентским iPhone) для просмотра предварительного просмотра, а также принимать команды с клиентского iPhone. Я думаю о стандартном способе достижения этого. Самое близкое, что я нашел, это AVCaptureMultipeerVideoDataOutput на Github.Одноранговое видео в iOS

Однако это все еще использует инфраструктуру подключения Multipeer, и я думаю, что она по-прежнему требует некоторой настройки на обоих iPhone. Я хочу, чтобы в обоих iPhone не было идеальной настройки, если Wi-Fi (или, если возможно, bluetooth) включен на обоих iPhone, одноранговые узлы должны распознавать друг друга в приложении и запрашивать пользователя об обнаружении устройства. Каковы стандартные способы достижения этого и любые ссылки на образец кода?

EDIT: Я получил возможность работать через соединение Multipeer после написания кода с нуля. На данный момент я отправляю буферы пикселей в одноранговое устройство путем уменьшения масштаба &, сжимающего данные как jpeg. На удаленном устройстве у меня есть настройка UIImage, где я показываю данные за каждое время кадра. Однако я думаю, что UIKit может быть не лучшим способом отображения данных, хотя изображения небольшие. Как показать эти данные с помощью OpenGLES? Возможно ли прямое декодирование jpeg в Opengles?

+0

У AFAIK multipeer connectivity есть все API, необходимые для вашего требования к открытию и потоковым живым сеансам. Если вы не хотите использовать его, другой вариант будет использовать AirPlay, но это обходное решение и не сможет помочь в обнаружении близких клиентов iPhones. – Bluewings

+0

Я пробовал образцы кода MC, но не смог соединить устройства и отправить данные. Мне нужен простой интерфейс, где один iPhone выдает всплывающее окно, чтобы выбрать однорангового узла, как только он будет обнаружен. Выбор однорангового узла запускает поток данных. Я не могу заставить его работать через любой из доступных образцов кодов, в том числе один из Apple в групповом чате 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 просто: вы хотите, чтобы кадры отображались с равными интервалами времени.В противном случае видео, кажется, замедляется сначала порой, прямо перед тем, как ускорить путь назад, чтобы догнать. Моя схема гарантирует, что каждый кадр воспроизводится один за другим с одинаковым темпом, независимо от узких мест в пропускной способности сети.