本质就是图像传输,在次基础上的扩展。
其实就是图像一帧一帧的传输,一直下去就能达到实时图像传输的效果了。
具体实现
socket对象调用 getInputStream()方法获得一个输入流 inputStream,同时使用 FileOutputStream(file)方法创建一个文件输出流用来保存图片数据其中 file为图片的绝对路径,之后对 inputStream不断调用 read ()方法进行数据读取,并且将读取的数据通过输出流的 write()方法写入本地,本地则是通过handle来获得的图片并通过BitmapFactory.decodeFile函数来得到一个Bitmap图像,最后通过进行相应比例的放大,通过 paint(画笔工具)在canvas(画布)上绘制并提交画布显示到 SurfaceView上。
TCP 、socket ,java文件流,android基础、SurfaceView
TCP接受部分函数 image_transmission()
这里只是贴了图像接受部分修改的函数,并不是具体实现的文件,具体实现可参考[java图像传输]。(https://blog.csdn.net/NS_ice/article/details/124306479?spm=1001.2014.3001.5502)
// 图像传输客户端程序 这个服务端接受线程中使用的函数
private void image_transmission() throws IOException {
File file = new File("data/data/com.example.uidemo4/1.jpg");
//com.example.uidemo4 是安卓软件的包名 这个文件夹下的文件 app才有权限进行读取和修改
if (!file.exists()) { //exists() 判断是否存在,存在返回true
System.out.println("建立此文件");
boolean a= false; //不存在就创建一个,空文件
try {
a = file.createNewFile(); //文件不存在且新建成功会返回true
} catch (IOException e) {
e.printStackTrace();
}
if (a) {
System.out.println(file.toString().trim());
}
}
inputStream = socket.getInputStream();
FileOutputStream fos = null;
byte[] temByte = new byte[2048]; // 读取缓冲数组
byte[] preByte = new byte[15]; //数据头
byte[] sizeByte = new byte[5]; //图片大小
byte[] endByte = new byte[3]; // 数据尾
int all = 0;// 已读取的文件总长度
while (flag) {
int len;
len = inputStream.read(preByte);//读取数据头
String readTextString = new String(preByte, "UTF-8");
if (readTextString.equals("start")) {
// System.out.println("开始接收图像");
len = inputStream.read(sizeByte); //读取图片大小
String a = new String(sizeByte, "UTF-8");
int length = Integer.parseInt(a);
// System.out.println("图片大小为:" + a);
fos = new FileOutputStream(file);
for (all = 0;all<length;) {
if(all+2048<=length) {
// System.out.println("剩余部分大于1024");
len = inputStream.read(temByte);
all = all +len;
fos.write(temByte, 0, len); //通过文件流写入文件
// System.out.println("读取到"+len +"剩余"+(length-all));
}else{
len = inputStream.read(temByte,0,length-all);
all = all +len;
fos.write(temByte, 0, len);
// System.out.println("读取到"+len +"剩余"+(length-all));
if (all ==length) {
//加这个判断的原因是及时你设置读取数组大小为2048,
//但是每次读取的可能会比他小(可能由于网络原因)
len = inputStream.read(endByte);
readTextString = new String(endByte, "UTF-8");
// System.out.println("结束为:" + readTextString);
if (readTextString.equals("end")) {
// System.out.println("接收到图片");
//确保读取完毕后通过handler发送消息在handler中更新ui
handler.sendEmptyMessage(MyHandler.READ_DATA);
//发送空内容消息,触发绘图 msg.what = READ_DATA =9;
fos.close();
//关闭文件流,循环开始时会创建,你可能觉得为啥不一直用一个
//用一个流没有结束,好像就那个图片文件不算传输结束了,后面无法使用该文件
break;
}
}
}//else
} //for (all = 0;all
} //if (readTextString.equals("start"))
}//while (flag)
}//image_transmission()
注意这里的接受,发送端需要根据接受端的接受顺序来发送数据
发送顺序为: start (数据头字符串)+ 图片大小(字符串)+ 图片文件 + end (数据结束尾)
在图片大小前和数据头前还可以加入字符串数据,图片文件尾后和end前也可以加入字符串数据,接收方法和数据头尾类似。
直接复制代码会报错:
MyHandler handler = new MyHandler(MainActivity.this ,mSurfaceholder);
此外 surfaceholder需要通过xml中的控件SurfaceView来获取。
//全局变量
private SurfaceView mCameraView; //surfaceView
private SurfaceHolder mSurfaceholder;
//onCreate函数中通过id获取控件
mSurfaceView =findViewById(R.id.surface_view);
mSurfaceholder = mSurfaceView .getHolder();
xml文件 布局文件
大小宽度布局自己根据需求来设置
<SurfaceView
android:id="@+id/surface_view"
android:layout_width="320dp"
android:layout_height="240dp"
android:layout_margin="30dp"
/>
用于更新ui界面,详情可以去网上搜具体功能(资源很多),这里只简单介绍使用。
public class MyHandler extends Handler
{
public static final int READ_DATA = 9; //数字没有具体意义就是为了区分消息的作用,用于下方switch判断。
private Rect mSrcRect, mDestRect;
private SurfaceHolder surfaceholder;
//private SurfaceView surfaceView;
private Paint paint; //画笔工具,理解就好,没有特别需要注意的
private Bitmap bitmap; // 图片文件,用户绘制
private Canvas canvas; //画布工具,能用就行
// 构造方法 可根据需求自己创建
MyHandler(Context context, SurfaceHolder surfaceholder)
{
this.context = context;
this.surfaceholder = surfaceholder;
}
@Override
public void handleMessage(@NonNull Message msg)
{
switch (msg.what)
{
case READ_DATA:
try {
canvas = surfaceholder.lockCanvas();//锁定画布
if(canvas!=null){
// System.out.println("开始绘制");
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
// canvas.drawColor(Color.WHITE); //绘制背景
bitmap = BitmapFactory.decodeFile("data/data/com.example.uidemo4/1.jpg"); // 获取bitmap
// 文件路径就是上面接受端保存路径的位置
mSrcRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
mDestRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());
//这两句是用来获取图片大小和画布大小,在下面绘制函数中用到的参数,使用这个可以让图片填充到画布相同大小
//这里的画布大小,其实就是你xml文件中SurfaceHolder的大小
canvas.drawBitmap(bitmap, mSrcRect, mDestRect, paint); //执行绘制操作
// canvas.drawBitmap(bitmap, 0, 0, paint); //不调节图像与画布比例,直接画原图
}
}catch (Exception e){
}finally {
if (canvas != null) {
surfaceholder.unlockCanvasAndPost(canvas);//解锁并提交画布
// System.out.println("绘制完成");
if (bitmap!=null){
bitmap.recycle();}
break;
}
}
}
以上就是图像实时传输的主要实现代码,并没有把完整工程文件代码贴出来,显得冗余,主要是提供一个思路和核心代码实现吧,代码中注释还是比较详细的,有不理解的可以网上查或者评论区留言,看到就会回复的,觉得还不错的话可以高抬贵手点个赞。有需要话可以整个手把手教程。