更新 - 我已经修复了下面代码中的一些错误,并且图像显示在另一台设备上,但我还有另一个问题。当视频捕获打开时,“主”设备连续发送数据,有时此捕获出现在“从”设备上,并且在很短的时间内,图像“闪烁”为空白并在短时间内重复此操作。对此有任何想法吗?
我正在开发一个需要将实时摄像头捕获和实时麦克风捕获发送到网络中的另一台设备的应用程序。
我已经使用 TCP 服务器完成了设备之间的连接并使用 bonjour 发布它,这就像一个魅力。
最重要的部分是从“主”设备发送和接收视频和音频,并在“从”设备上渲染。
首先,这里有一段代码,应用程序在其中获取相机样本缓冲区并在 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
过了一会儿,我收到了这条消息。
关键是找到图像没有显示在“从”设备上的原因。
谢谢。