因此,我无法为上述问题找到一个完整且绝对完美的解决方案,但为了任何其他可能偶然发现类似的跨语言客户端-服务器程序问题的快速初学者的利益,这里是我的两分钱:
上述代码中的第一个也是最重要的错误是这一行:
let data = UIImageJPEGRepresentation(image, 1.0)
1)在这里,我通过提供压缩因子为 1 将 UIImage 编码为尽可能高的质量。正如我后来检查的那样,这导致创建计数超过 100000 的字节数组,因此很难轻松快速地创建通过 TCPClient 套接字发送这么大的数据。
2)即使这么大的数组被TCPClient的socket高效的发送出去,服务器端的Java DataInputStream也很难一次性读取完整的数据。它可能一次只读取小块数据,因此在 java 服务器端生成的图像是部分和模糊的。
3)这条线是另一个问题:
count = in.available();
if(count>0) System.out.println("LENGTH="+count);
byte[] arr=new byte[count];
System.out.println("byte="+arr);
in.read(arr);
in.available ()方法可能不会返回客户端发送的完整数据长度。这会导致读取不完整的字节数据,从而导致不完整的图像。
解决方案/解决方法(种类)
我在 swift 客户端的 UIImageJPEGRepresentation() 方法中将压缩因子降低到大约 0.000005,这导致创建长度为 ~ 5000 的字节数组(这是可管理的)
为了避免在服务器端读取不完整数据的问题,我将字节数组转换为 base64String,然后我只是在该字符串的末尾添加了一个终止字符“%”,它在服务器端将标记一个 base64 字符串的结尾.
我将服务器端的 DataInputStream/DataOutputStream 更改为 InputStreamReader/OutputStreamWriter,因为我现在正在处理字符/字符串。
Java 服务器的 InputStreamReader 将一次接收一个字符并从中形成一个字符串,直到它遇到终止字符“%”,然后这个 base64string 将被转换为字节数组:
imageBytes=javax.xml.bind.DatatypeConverter.parseBase64Binary(str);
//str is a String formed by concatenating characters received by the InputStreamReader
然后将这个 imageBytes 数组转换成 BufferedImage,然后在面板上一个接一个地绘制,从而再现原始 iPhone 实时视频
修改后的 Swift 代码(ios 客户端)
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!)
{
var cameraImage: CIImage
var image: UIImage ;
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
cameraImage = CIImage(cvPixelBuffer: pixelBuffer!)
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(cameraImage, from: cameraImage.extent)!
image = UIImage(cgImage: cgImage)
DispatchQueue.main.async
{
self.imageView.image = image //live video captured from camera streamed to the device's own UIImageView
}
let thumbnail = resizeImage(image: image, targetSize: CGSize.init(width: 400, height: 400)) // snapshot image from camera resized
let data = UIImageJPEGRepresentation(thumbnail,0.000005) //the snapshot image converted into byte data
let base64String = data!.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
// byte image data is encoded to a base64String
var encodeImg=base64String.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed )
encodeImg = encodeImg! + String("%") // termination char % is added at the end
var sendData = String("0%")
if(live)
{
sendData = encodeImg!
}
client?.send(string: sendData!) //sent as a String using TCPClient socket
}
修改了 MobileServer Thread 类的 Java 服务器端 run() 方法
public void run()
{
try{
boolean access_granted=false;
while(!stop)
{
char chr=(char)in.read();
if(chr!='%') // read and append char by char from the InputStreamReader "in" until it encounters a '%'
str+=Character.toString(chr);
else terminate=true;
if(terminate)
{
if(entry)
{
int a=str.indexOf('&');
int b=str.indexOf('#');
String username=str.substring(0,a);
String password=str.substring((a+1),b);
String ip=str.substring((b+1),str.length());
System.out.println("IP ADDRESS: \""+ ip+"\"");
String usernameA[]=convertToArray(username);
String passwordA[]=convertToArray(password);
String user=decrypt(usernameA,portt);
String pass=decrypt(passwordA,portt);
boolean accessGranted=false;
int response=dbManager.verify_clientLogin(user,pass);
if(response==RegisterInfo.ACCESS_GRANTED) {
System.out.println("access granted");
accessGranted=true;
}
int retInt=-1;
if(accessGranted) retInt=1;
out.write(retInt);
entry=false;
terminate=false;
}
else
{
terminate=false;
try {
// str includes the original single base64String produced by the swift client app which is converted back to a byte array
imageBytes = javax.xml.bind.DatatypeConverter.parseBase64Binary(str);
}catch(ArrayIndexOutOfBoundsException l){ exception=true; }
str="";
if(!exception)
{
//this byte array image data is converted to a image and played on the videoPlayer, and serial images played would be visible as a video stream
vidPlayer.playImage(imageBytes);
ioexcep=false;
}
else exception=false;
}
}
}
}catch(Exception l){ l.printStackTrace(); }
}
这是正在播放的视频的截图:

但是正如您所看到的,因为图像是从 swift 客户端以非常低质量发送的:生成的视频质量也很差。另外,视频仍然挂在两者之间..
我相信会有更好的方法从 swift 套接字发送更高质量的图像数据,市场上所有这些视频聊天应用程序都证明了这一点,如果有人能对所涉及的高级方法有所了解以实现高清图像传输
一种方法是在客户端和服务器端缓冲字节数据以传输和播放更高质量的 jpeg 数据