博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
图片播放技术总结
阅读量:5347 次
发布时间:2019-06-15

本文共 5248 字,大约阅读时间需要 17 分钟。

1 概述

由于工作的项目上的需求,需要在浏览器上不间断的播放图片,即像播放视频一样播放图片。

后端支持采用Java实现,需要用Java编写一个Http服务器,并提供WebSocket服务。前后端通过Http链接或WebSocket提供图片浏览服务,前端采用JS轮询或WebSocket推送的方式获取图片,浏览器显示图片有两种方式:一种是采用连续切换图片源,实现播放效果;另一种采用将图片画在canvas上面,实现播放。

要完成这个功能涉及到以下技术:

  • Http服务器的实现
  • 高速的读文件
  • WebSocket原理及实现
  • 基于浏览器pull方式的http资源获取
  • 基于服务器端push方式的http资源获取
  • JS播放图片帧的性能

2 技术分析

2.1 Http服务器的实现

实现HTTP服务器比较容易,实现方式也有如下多种:

  • 基于jdk中com.sun包下面的HttpServer来实现。(不推荐,com.sun不在java规范内,jdk升级可能会不兼容)
  • 基于jetty或tomcat的嵌入式包来实现。此方式基于Servlet规范来实现的,较简单且易于理解。
  • 基于netty的方式实现。性能好,需要对NIO有了解,编程难度相对大一些。
  • 基于vert.x的实现。这种方式底层还是采用netty实现,较简单,但是也需要熟悉vert.x的编程模型。

我们采用jetty的方式实现,基于servlet3.0规范,可以支持异步请求方式。代码如下:

public static void main(String[] args){    Server server = new Server();    ServerConnector connector = new ServerConnector(server);    connector.setPort(8080);    server.addConnector(connector);    ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);    context.setContextPath("/");    context.addServlet(new ServletHolder(new HelloServlet()), "/hello");      server.setHandler(context);    try {        // Initialize javax.websocket layer        ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);        // Add WebSocket endpoint to javax.websocket layer        wscontainer.addEndpoint(EventSocket.class);        server.start();        server.dump(System.err);        server.join();    } catch (Throwable t){        t.printStackTrace(System.err);    }}

2.2 高速的读文件

关于Java高速读取文件可参考这篇文章:

这篇文章得出四个结论:

  • 为了减少I/O操作,每次应该读一个byte数组,而不是一个byte字节,8K的byte数组就是一个好的选择。
  • 为了减少方法调用的开销,每次应该获取一个byte数组的数据,而不是一个byte字节。
  • 为了减少线程同步锁的开销,要么减少线程同步方法的调用,要么采用非线程安全的类,如:FileChannel 和MappedByteBuffer。
  • 为了减少在JVM/OS、internal buffers和应用程序数组之间的数据拷贝,要么使用带有内存映射的FileChannel类,要么使用a direct or wrapped array ByteBuffer.

下面提高两种高速读取文件方法:

for (String filePath : fileList){    try(FileChannel ch = new RandomAccessFile(filePath, "r").getChannel()){        int size = (int) ch.size();        MappedByteBuffer buf = ch.map(MapMode.READ_ONLY, 0, size);        // 处理buf....    } catch (IOException e) {        e.printStackTrace();    }}
for (String filePath : fileList){    try (SeekableByteChannel sbc = Files.newByteChannel(Paths.get(filePath), StandardOpenOption.READ)) {        ByteBuffer buf = ByteBuffer.allocate(10);        // Read the bytes with the proper encoding for this platform.  If        // you skip this step, you might see something that looks like        // Chinese characters when you expect Latin-style characters.        //String encoding = System.getProperty("file.encoding");        while (sbc.read(buf) > 0) {            buf.rewind();            // 处理buf...            //System.out.print(Charset.forName(encoding).decode(buf));            buf.flip();        }    } catch (IOException x) {        System.out.println("caught exception: " + x);    }}

2.3 WebSocket原理及实现

WebSocket的原理以及与Http区别可以参考:,总体来说,原理及区别如下:

  • WebSocket和Http协议没有太大的关系,WS只是借助Http实现了第一次握手,之后从http协议upgrade为ws://协议。
  • WS是持久性连接(类似socket),而HTTP的短连接、长连接都不是持久的。
  • WS协议是支持全双工的,可以pull,亦可以push。

用Java实现WebSocket服务端:

@ClientEndpoint@ServerEndpoint(value="/events/")public class EventSocket{    private static int DEFAULT_BUFFER_SIZE = 128 * 1024;// 8192    private byte[] bytes;    @OnOpen    public void onWebSocketConnect(Session session, EndpointConfig config)  {        session.setMaxBinaryMessageBufferSize(DEFAULT_BUFFER_SIZE);    }    @OnMessage    public void onWebSocketText(Session session, String message) throws Exception{        System.out.println("Received TEXT message: " + message);        bytes = ... // 读取图片文件字节        // 发送图片文件        session.getAsyncRemote().sendBinary( ByteBuffer.wrap( this.bytes ) );    }    @OnClose    public void onWebSocketClose(Session session, CloseReason reason){        System.out.println("Socket Closed: " + reason);    }    @OnError    public void onWebSocketError(Session session, Throwable cause){        cause.printStackTrace(System.err);    }}

2.4 浏览器并发请求与长连接

浏览器请求一般都是拉取服务器的资源,而请求方式分为短连接和长连接两种,这篇文章介绍很清楚:

浏览器对后端资源的请求都是并发的执行的,不同的浏览器并发连接数不同。现在大多数浏览器都支持http1.1协议,默认都会开启keep-alive,支持长连接。在浏览器对后端的资源发出请求,在开启keep-alive情况下,都会复用连接通道。如果是不间断的下载图片,应该使用的是长连接通道复用功能。

2.5 Web服务器Push技术

实现服务器端Push有以下几种方式:

  • Ajax轮询。采用setInterval方法不停的调用
  • Ajax长轮询。俗称Comet方式,不需要重复建立连接,没有响应就一直等,等到才关闭连接。
  • WebSocket
  • server-sent-events

Ajax轮询原理还是pull的方式,不算真正的push,但是对一些老版本的浏览器是适用的。WebSocket优点是支持全双工、可跨域。server-sent-server实现简单,但只支持server到client单向传输,且IE系列都不支持。详细内容参考:

除了上面一些方法外,还有一些其他方式,如:Flash XML Socket, Java Applet等非主流。

而在本案例中,如果采用WebSocket传送图片,可实现真正的服务器端不间断的推送图片数据,但是如果要实现并发传送,必须自己在浏览器端来实现,否则,仅仅单连接的情况下不一定比浏览器的并发连接快。

2.6 JS播放图片帧的性能

JS播放图片有多种方式,如:

  • 采用标签,不停改变img的src属性,实现播放。
  • 采用Html5的Canvas,将Image对象画在Canvas上,实现播放。
  • 将图片设置为Div的背景,不停的更换背景,实现播放。

采用标签方式实现如下:

(function() {    var i = 0;    var pics = [ "andy_white.jpg", "andy_black.jpg" ];    var el = document.getElementById('img_to_flip');  // el doesn't change    function toggle() {        el.src = pics[i];           // set the image        i = (i + 1) % pics.length;  // update the counter    }    setInterval(toggle, 2000);})();

这种方式下浏览器CPU占用率非常高,在IE11和Chrome下,i3的CPU(T440P)占用都在60%左右,内存占用较少,大约在100M左右。CPU的消耗主要在浏览器对图片的渲染上。

用Canvas替代标签,CPU占用方面,IE11仍然占用那么高,Chrome能降一半。更换背景的方式没有实验。基于以上,采用Canvas的方式是一种比较好的选择。

aaaa

bbbb

转载于:https://www.cnblogs.com/gjhuai/p/6807492.html

你可能感兴趣的文章
Vue学习指南
查看>>
前端通信小结
查看>>
js字符替换等操作
查看>>
Subsequence(HDU3530+单调队列)
查看>>
JS倒计时网页自动跳转代码
查看>>
mysql中find_in_set()函数的使用
查看>>
九度 1495:关键点(图论)
查看>>
日常时间安排
查看>>
Mac下配置phpstorm
查看>>
Oracle 数值函数
查看>>
SVN文件自动加锁-Win7
查看>>
《编程珠玑》和《梦断代码》(部分) 读书笔记
查看>>
构造 HDOJ 5400 Arithmetic Sequence
查看>>
BestCoder Round #75
查看>>
MythXinWCF通用宿主绿色版V1.1
查看>>
Windows下根据端口号杀死进程
查看>>
使用linux操作系统的公司服务器有哪些品牌
查看>>
Hello ,移动WEB 笔记
查看>>
JS与IE/Firefox兼容性汇总
查看>>
面试概念集锦
查看>>