在傳輸視頻音頻的時候一般選用UDP協議,因爲TCP會檢查傳輸的數據包是否正確,當錯誤時要求重傳,這樣在觀看實時視頻時會造成卡頓。UDP並不會去要求重傳,它會把錯誤的直接丟掉,而丟掉少許的數據包對我們觀看視頻來說並不會造成太大的影響,所以選用UDP協議。
傳輸的Python代碼如下:
import cv2
import socket
sever = socket.socket()
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #創建socket對象
ser=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #創建socket對象
server.bind(("",8888))
filePath = r"F:\Python\picture\vtest.avi"
camera = cv2.VideoCapture(filePath)
re = server.recvfrom(2048)
while True:
ret,frame = camera.read()
if not ret:
break
frame = cv2.resize(frame,(1000,700))
data = cv2.imencode(".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 15])[1]
ser.sendto(data,(re[1][0],9999))
server.close()
ser.close()
imencode將傳輸的每一幀編碼爲JPG格式,後面的15是壓縮率,取值在1到100之間,並且數值越大傳輸效果越好。
Android端代碼如下:
package com.example.bishe;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class MainActivity extends AppCompatActivity {
private final static String SEND_IP = "192.168.2.110"; //發送IP
private final static int SEND_PORT = 8888; //發送端口號
private final static int RECEIVE_PORT = 9999; //接收端口號
private boolean listenStatus = true; //接收線程的循環標識
private byte[] buf;
Bitmap bp;
private DatagramSocket receiveSocket;
private DatagramSocket sendSocket;
private DatagramSocket reveSocket;
private InetAddress serverAddr;
private SendHandler sendHandler = new SendHandler();
private ReceiveHandler receiveHandler = new ReceiveHandler();
private ImageView imgShow;
private Button btn;
class ReceiveHandler extends Handler{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
imgShow.setImageBitmap(bp);
}
}
class SendHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgShow = findViewById(R.id.img_show);
btn = findViewById(R.id.btn_send);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new UdpSendThread().start();
}
});
new UdpReceiveThread().start();
}
/*
* UDP數據接受線程
* */
public class UdpReceiveThread extends Thread
{
@Override
public void run()
{
try
{
reveSocket = new DatagramSocket(RECEIVE_PORT);
serverAddr = InetAddress.getByName(SEND_IP);
//ByteArrayOutputStream out = new ByteArrayOutputStream();
while(listenStatus)
{
byte[] inBuf= new byte[1024*1024];
DatagramPacket inPacket=new DatagramPacket(inBuf,inBuf.length);
//out.write(inPacket.getData());
reveSocket.receive(inPacket);
if(!inPacket.getAddress().equals(serverAddr)){
throw new IOException("未知名的報文");
}
ByteArrayInputStream in = new ByteArrayInputStream(inPacket.getData());
receiveHandler.sendEmptyMessage(1);
bp = BitmapFactory.decodeStream(in);
}
} catch (Exception e)
{
e.printStackTrace();
}finally {
reveSocket.close();
}
}
}
/*
* UDP數據發送線程
* */
public class UdpSendThread extends Thread {
@Override
public void run() {
try {
buf = "i am an android developer, hello android! ".getBytes();
// 創建DatagramSocket對象,使用端口8888
sendSocket = new DatagramSocket(8888);
serverAddr = InetAddress.getByName(SEND_IP);
DatagramPacket outPacket = new DatagramPacket(buf, buf.length, serverAddr, SEND_PORT);
sendSocket.send(outPacket);
sendSocket.close();
sendHandler.sendEmptyMessage(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
佈局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_margin="5dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:id="@+id/img_show"
android:layout_marginTop="30dp"
android:background="#000000"
android:layout_height="300dp">
</ImageView>
<Button
android:layout_width="wrap_content"
android:layout_marginTop="10dp"
android:id="@+id/btn_send"
android:text="鏈接到服務器"
android:textSize="30sp"
android:layout_height="wrap_content">
</Button>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
效果圖: