通过 NSInputStream 和 NSOutputStream 进行视频流
Posted
技术标签:
【中文标题】通过 NSInputStream 和 NSOutputStream 进行视频流【英文标题】:Video streaming via NSInputStream and NSOutputStream 【发布时间】:2017-02-21 14:19:14 【问题描述】:现在我正在研究通过 MultipeerConnectivity 框架实现视频流的可能性。为此,我使用 NSInputStream 和 NSOutputStream。
问题是:我目前收不到任何图片。现在我正在尝试传递简单的图片并将其显示在接收器上。这是我的代码的一个小sn-p:
通过 NSOutputStream 发送图片:
- (void)sendMessageToStream
NSData *imgData = UIImagePNGRepresentation(_testImage);
int img_length = (int)[imgData length];
NSMutableData *msgData = [[NSMutableData alloc] initWithBytes:&img_length length:sizeof(img_length)];
[msgData appendData:imgData];
int msg_length = (int)[msgData length];
uint8_t *readBytes = (uint8_t *)[msgData bytes];
uint8_t buf[msg_length];
(void)memcpy(buf, readBytes, msg_length);
int stream_len = [_stream writeData:(uint8_t*)buf maxLength:msg_length];
//int stream_len = [_stream writeData:(uint8_t *)buf maxLength:data_length];
//NSLog(@"stream_len = %d", stream_len);
_tmpCounter++;
dispatch_async(dispatch_get_main_queue(), ^
_lblOperationsCounter.text = [NSString stringWithFormat:@"Sent: %ld", (long)_tmpCounter];
);
上面的代码运行良好。写入后的 stream_len 参数等于 29627 字节,这是预期值,因为图像的大小约为 25-26 kb。
通过 NSinputStream 接收图片:
- (void)readDataFromStream
UInt32 length;
if (_currentFrameSize == 0)
uint8_t frameSize[4];
length = [_stream readData:frameSize maxLength:sizeof(int)];
unsigned int b = frameSize[3];
b <<= 8;
b |= frameSize[2];
b <<= 8;
b |= frameSize[1];
b <<= 8;
b |= frameSize[0];
_currentFrameSize = b;
uint8_t bytes[1024];
length = [_stream readData:bytes maxLength:1024];
[_frameData appendBytes:bytes length:length];
if ([_frameData length] >= _currentFrameSize)
UIImage *img = [UIImage imageWithData:_frameData];
NSLog(@"SETUP IMAGE!");
_imgView.image = img;
_currentFrameSize = 0;
[_frameData setLength:0];
_tmpCounter++;
dispatch_async(dispatch_get_main_queue(), ^
_lblOperationsCounter.text = [NSString stringWithFormat:@"Received: %ld", (long)_tmpCounter];
);
如您所见,我尝试分几个步骤接收图片,原因如下。当我尝试从流中读取数据时,无论我在 maxLength:
参数中输入什么数字,它总是读取最大 1095 个字节。但是当我在代码的第一个 sn-p 中发送图片时,它发送绝对没问题(29627 字节。顺便说一句,图像的大小约为 29 kb。
这就是我提出问题的地方 - 为什么会这样?当接收导致问题时,为什么通过 NSOutputStream 发送 29 kb 工作正常?有没有一种可靠的方法可以通过 NSInputStream 和 NSOutputStream 使视频流工作?我只是没有找到太多关于这项技术的信息,我发现的只是一些我已经知道的简单的东西。
【问题讨论】:
顺便说一句,流正在相互协作。我忘了提那个。因此可以排除连接不良的问题。我用简单的字符串测试了流,它工作得非常好 【参考方案1】:这是我编写的一个应用程序,它向您展示了如何:
https://app.box.com/s/94dcm9qjk8giuar08305qspdbe0pc784
使用 Xcode 9 构建项目并在两台 ios 11 设备上运行该应用。
要流式传输实时视频,请触摸两台设备之一上的相机图标。
如果您没有两台设备,您可以在模拟器中运行一个应用程序;但是,您只能在真机上使用相机(模拟器会显示播放的视频)。
您知道:这不是在设备之间流式传输实时视频的理想方式(这可能是您的最后选择)。数据包(相对于流式传输)更高效、更快。
无论如何,我真的对你的 NSInputStream 相关代码感到困惑。我认为这里有一些更有意义的东西:
case NSStreamEventHasBytesAvailable:
// len is a global variable set to a non-zero value;
// mdata is a NSMutableData object that is reset when a new input
// stream is created.
// displayImage is a block that accepts the image data and a reference
// to the layer on which the image will be rendered
uint8_t * buf[len];
len = [aStream read:(uint8_t *)buf maxLength:len];
if (len > 0)
[mdata appendBytes:(const void *)buf length:len];
else
displayImage(mdata, wLayer);
break;
输出流代码应如下所示:
// data is an NSData object that contains the image data from the video
// camera;
// len is a global variable set to a non-zero value
// byteIndex is a global variable set to zero each time a new output
// stream is created
if (data.length > 0 && len >= 0 && (byteIndex <= data.length))
len = (data.length - byteIndex) < DATA_LENGTH ? (data.length - byteIndex) : DATA_LENGTH;
uint8_t * bytes[len];
[data getBytes:&bytes range:NSMakeRange(byteIndex, len)];
byteIndex += [oStream write:(const uint8_t *)bytes maxLength:len];
除了正确设置 NSStream 类之外,流式传输视频还有很多其他内容 - 更多内容。您会注意到在我的应用程序中,我为输入和输出流创建了一个缓存。如果您不这样做,这解决了您可能会遇到的无数问题。
我从未见过有人成功地使用 NSStreams 进行视频流传输……从来没有。它非常复杂,原因之一。
流式传输视频有许多不同(和更好)的方式;我不会走这条路。我只是接受它,因为没有其他人能够成功地做到这一点。
【讨论】:
【参考方案2】:我认为问题在于您假设所有数据都将在您阅读时始终在NSInputStream
中可用。由NSURL
对象制成的NSInputStream
具有异步特性,因此应使用NSStreamDelegate
对其进行访问。您可以查看POSInputStreamLibrary 的自述文件中的示例。
【讨论】:
感谢您的回复!我正在通过 NSStreamDelegate 方法严格使用我的 NSInputStream,我根本不做任何解决方法。所以当我阅读 NSInputStream 时,它 100% 有一些数据要提供。主要问题是关于传递数据的长度。为什么我可以一次调用 NSOutputStream 传递 29kb 的图片,但我不能在读取时对 NSInputStream 做同样的事情? 因为你不需要。 //// len 是一个设置为非零值的全局变量; //// mdata 是一个 NSMutableData 对象,它在创建新输入 //// 流时被重置。 //// displayImage 是一个块,它接受图像数据和引用 //// 到将在其上渲染图像的层 uint8_t * buf[len]; len = [aStream read:(uint8_t *)buf maxLength:len]; if (len > 0) [mdata appendBytes:(const void *)buf length:len]; else displayImage(mdata, wLayer); 休息;以上是关于通过 NSInputStream 和 NSOutputStream 进行视频流的主要内容,如果未能解决你的问题,请参考以下文章
将 CFReadStreamRef 转换/转换为 NSInputStream (iOS5)
坚持使用hasSpaceAvailable并且无法从NSInputStream中读取
未能从 Swift 继承 NSInputStream(initWithData:无法识别的选择器)