1

한 iPhone에서 실시간 비디오 버퍼를 미리보기 용으로 다른 iPhone (클라이언트 iPhone이라고 함)에 전송하고 클라이언트 iPhone에서 명령을 수락하려고합니다. 나는 이것을 달성하기위한 표준 방법을 생각하고있다. 내가 찾은 가장 가까운 것은 에 AVCaptureMultipeerVideoDataOutput입니다.iOS의 피어 투 피어 비디오

그러나 Multipeer 연결 프레임 워크를 여전히 사용하고 있으며 두 대의 iPhone에서 여전히 설정이 필요하다고 생각합니다. 내가 원하는 것은 두 아이폰 모두 Wifi (가능하다면 블루투스)가 두 아이폰에서 모두 활성화되어있는 한 두 아이폰에 이상적인 설정이 필요하지 않다는 것입니다. 동료는 앱 내에서 서로를 인식하고 사용자에게 장치 검색을 묻습니다. 이것을 달성하기위한 표준 방법과 샘플 코드에 대한 링크는 무엇입니까?

편집 : 처음부터 코드를 작성한 후 Multipeer 연결을 통해 작업 할 수 있습니다. 지금은 픽셀 버퍼를 & 크기를 jpeg로 압축하여 피어 장치로 보냅니다. 원격 장치에서 모든 프레임 시간마다 데이터를 표시하는 UIImage 설치가 있습니다. 그러나 UIKit은 이미지가 작더라도 데이터를 표시하는 가장 좋은 방법이 아닐 수도 있습니다. OpenGLES를 사용하여이 데이터를 어떻게 표시합니까? Opengles에서 jpeg의 직접 디코딩이 가능합니까?

+0

AFAIK 멀티 페어 연결에는 검색 및 스트림 라이브 세션 요구 사항에 필요한 모든 API가 있습니다. 그것을 사용하고 싶지 않다면 AirPlay를 사용하는 다른 옵션이 있지만 해결 방법이며 클라이언트의 근처에있는 아이 검색에 도움을 줄 수는 없습니다 – Bluewings

+0

MC의 샘플 코드를 시도했지만 장치를 쌍으로 연결하여 보낼 수 없습니다 데이터. 내가 필요한 것은 하나의 아이폰이 발견되는 즉시 피어를 선택하기 위해 팝업을 던지는 간단한 인터페이스이다. 피어를 선택하면 데이터 스트림이 시작됩니다. MC 그룹 채팅에서 Apple이 제공하는 것을 포함하여 사용 가능한 샘플 코드를 통해 작동하지 못합니다. –

답변

1

코멘트 : 지금 현재로

는, I는 JPEG으로 데이터를 압축하여 & 다운 스케일링 장치 피어 화소 버퍼를 보낸다. 원격 장치에서 I 에는 프레임 시간마다 데이터를 표시하는 UIImage 설정이 있습니다. 그러나 이미지가 작아도 UIKit이 데이터를 표시하는 가장 좋은 방법이 아닐지도 모릅니다.

이것은 멀티 페어 연결 프레임 워크를 통해 이미지를 전송하는 가장 좋은 방법입니다. 모든 대안을 시도했습니다 :

  1. VideoToolbox를 사용하여 프레임을 압축했습니다. 너무 느린.
  2. 압축을 사용하여 프레임을 압축했습니다. 너무 느리지 만 더 좋습니다. iOS 기기 전송 이미지 데이터에

    : 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); 
    } 
    

    :

    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]; 
         }); 
        } 
    } 
    

    비록

날 # 2에 대한 몇 가지 코드를 제공하자 이 코드는 수신 측에서 원본 품질의 이미지를 생성하므로 실시간 재생에는 너무 느립니다.

을 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); 
       }); 
      } 
     }); 
    }); 
} 

그 이유는 이미지 데이터를 수신하는 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]; 
    } 
} 

여기에 그것을 할 수있는 가장 좋은 방법입니다 세마포어와 별도의 GCD 대기열은 간단합니다. 프레임을 동일한 시간 간격으로 표시하려고합니다.그렇지 않으면 비디오가 처음에는 속도가 느려지는 것처럼 보입니다. 내 구성표는 네트워크 대역폭 병목 현상과 상관없이 각 프레임이 동일한 속도로 순차적으로 재생되도록합니다.