Linux Motion でストリーミング配信している画像を
Firefoxで見てたら、ブラウザが固まってしまうことが多かったのです。

Javaのビューワを探してみたら cambozola.jar っていうアプレットがあるようです。
アプレットは嫌だなぁ。ってことでswingで作ってみました。


HTTP ストリームを実際に読み込んでいる部分のソースはこんな感じです。
画像の右下端が少し壊れちゃうバグがあるけど何とか見れます
※画像の右下端が乱れる不具合を修正しました

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package motionframe;

import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger;

/** * * @author moremagic */ public abstract class HttpChankedReader{ private URL url = null; public HttpChankedReader(String sUrl) throws MalformedURLException{ this(new URL(sUrl)); } public HttpChankedReader(URL url){ this.url = url;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    start();
}

<span class="synType">private</span> <span class="synType">boolean</span> stop = <span class="synConstant">true</span>;
<span class="synType">public</span> <span class="synType">void</span> stop(){
    stop = <span class="synConstant">true</span>;
}

<span class="synType">public</span> <span class="synType">boolean</span> start(){
    <span class="synStatement">if</span>(!stop){
        <span class="synStatement">return</span> <span class="synConstant">false</span>;
    }

    stop = <span class="synConstant">false</span>;
    <span class="synStatement">new</span> Thread(){
        <span class="synPreProc">@Override</span>
        <span class="synType">public</span> <span class="synType">void</span> run(){
            <span class="synStatement">try</span> {
                read();
            } <span class="synStatement">catch</span> (IOException ex) {
                Logger.getLogger(HttpChankedReader.<span class="synType">class</span>.getName()).log(Level.SEVERE, <span class="synConstant">null</span>, ex);
            }
        }
    }.start();

    <span class="synStatement">return</span> !stop;
}

<span class="synType">public</span> <span class="synType">boolean</span> isStop(){
    <span class="synStatement">return</span> stop;
}

<span class="synType">private</span> <span class="synType">final</span> <span class="synType">void</span> read() <span class="synType">throws</span> IOException {
    ByteArrayOutputStream bytes = <span class="synStatement">new</span> ByteArrayOutputStream();

    HttpURLConnection con = (HttpURLConnection) url.openConnection();
    System.out.println(con.getHeaderField(<span class="synConstant">&quot;Content-Type&quot;</span>));

    <span class="synStatement">try</span> (InputStream in = con.getInputStream()) {
        BufferedInputStream bin = <span class="synStatement">new</span> BufferedInputStream(in);

        <span class="synType">int</span> contentLength = -<span class="synConstant">1</span>;

        <span class="synType">int</span> cnt = <span class="synConstant">0</span>;
        <span class="synType">byte</span>[] buff = <span class="synStatement">new</span> <span class="synType">byte</span>[<span class="synConstant">1024</span>];
        <span class="synStatement">while</span> ((cnt = bin.read(buff, <span class="synConstant">0</span>, buff.length)) != -<span class="synConstant">1</span> &amp;&amp; !stop) {
            bytes.write(buff, <span class="synConstant">0</span>, cnt);
            <span class="synStatement">if</span> (contentLength &lt; <span class="synConstant">0</span>) {
                <span class="synStatement">if</span>(<span class="synStatement">new</span> String(bytes.toByteArray()).startsWith(<span class="synConstant">&quot;--Boundary&quot;</span>)){
                    <span class="synType">int</span> start = <span class="synStatement">new</span> String(bytes.toByteArray()).indexOf(<span class="synConstant">&quot;</span><span class="synSpecial">\r\n\r\n</span><span class="synConstant">&quot;</span>);
                    <span class="synStatement">if</span> (start != -<span class="synConstant">1</span>) {
                        <span class="synComment">//System.out.println(&quot;------------- Hit! ---------------  &quot; + start);</span>
                        contentLength = getContextLength(<span class="synStatement">new</span> String(bytes.toByteArray(), <span class="synConstant">0</span>, start)) - bytes.size();
                    }
                }<span class="synStatement">else</span>{
                    <span class="synComment">//System.out.println(&quot;NG[no start with --Boundary] &quot; + (char)bytes.toByteArray()[0]);</span>

                    <span class="synComment">//読み直し</span>
                    <span class="synType">int</span> idx = <span class="synStatement">new</span> String(bytes.toByteArray()).indexOf(<span class="synConstant">&quot;--Boundary&quot;</span>);
                    <span class="synStatement">if</span>(idx != -<span class="synConstant">1</span>){
                        <span class="synType">byte</span>[] buf = bytes.toByteArray();
                        bytes.reset();
                        bytes.write(buf, idx, buf.length - idx);

                        <span class="synComment">//System.out.println(&quot;読み直し shift idx &gt;&gt; &quot; + idx + &quot; &quot; + (char)bytes.toByteArray()[0]);</span>
                    }

                    contentLength = -<span class="synConstant">1</span>;
                }
            } <span class="synStatement">else</span> {
                contentLength -= cnt;
                <span class="synStatement">if</span> (contentLength &lt; <span class="synConstant">0</span>) {                       
                    <span class="synType">synchronized</span>(<span class="synType">this</span>){
                        <span class="synType">int</span> start = <span class="synStatement">new</span> String(bytes.toByteArray()).indexOf(<span class="synConstant">&quot;</span><span class="synSpecial">\r\n\r\n</span><span class="synConstant">&quot;</span>);

//Length 分のデータだけでは右下画像が欠けるため、Length+1 //readChankBytes(Arrays.copyOfRange(bytes.toByteArray(), start + 4 , bytes.size() - (start + 4))); byte[] data = Arrays.copyOfRange(bytes.toByteArray(), start + 4, getContextLength(new String(bytes.toByteArray(), 0 , start) + 1)); readChankBytes(data); } bytes.reset(); } } } } catch (IOException err) { err.printStackTrace(); } }

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<span class="synType">private</span> <span class="synType">static</span> <span class="synType">int</span> getContextLength(String header) {
    <span class="synType">int</span> ret = -<span class="synConstant">1</span>;
    <span class="synStatement">for</span> (String line : header.split(<span class="synConstant">&quot;</span><span class="synSpecial">\n</span><span class="synConstant">&quot;</span>)) {
        <span class="synStatement">if</span> (line.toUpperCase().trim().startsWith(<span class="synConstant">&quot;CONTENT-LENGTH:&quot;</span>)) {
            <span class="synStatement">try</span>{
                ret = Integer.parseInt(line.substring(line.lastIndexOf(<span class="synConstant">&quot;:&quot;</span>) + <span class="synConstant">1</span>).trim());
            }<span class="synStatement">catch</span>(Exception err){
                <span class="synComment">//No Check.</span>
            }
        }
    }
    <span class="synStatement">return</span> ret;
}

<span class="synType">abstract</span> <span class="synType">void</span> readChankBytes(<span class="synType">byte</span>[] data);

}