Giter VIP home page Giter VIP logo

jersey-streaming's Introduction

Jersey 2 Media Streaming Application

Example showing how to implement a media streaming resource in Jersey 2 JAX-RS application using Jetty connector.

Partial content using range headers is the recommended approach to stream media. Here is a simple implementation that streams an audio file. The core of the logic lies in detecting whether clients support range requests. Some browsers send range headers when they detect specific media type such as "audio/mp3", "video/mp4" etc. response from the server. Chrome, Safari, Opera, Edge make use of these range headers when streaming media.

        // Firefox does not send range headers
        if (range == null) {
            StreamingOutput streamer = output -> {
                try (FileChannel inputChannel = new FileInputStream(asset).getChannel(); WritableByteChannel outputChannel = Channels.newChannel(output)) {
                    inputChannel.transferTo(0, inputChannel.size(), outputChannel);
                }
            };
            return Response.ok(streamer).status(200).header(HttpHeaders.CONTENT_LENGTH, asset.length()).build();
        }

        String[] ranges = range.split("=")[1].split("-");
        final int from = Integer.parseInt(ranges[0]);

        /*
          Chunk media if the range upper bound is unspecified. Chrome, Opera sends "bytes=0-"
         */
        int to = chunk_size + from;
        if (to >= asset.length()) {
            to = (int) (asset.length() - 1);
        }
        if (ranges.length == 2) {
            to = Integer.parseInt(ranges[1]);
        }

        final String responseRange = String.format("bytes %d-%d/%d", from, to, asset.length());
        final RandomAccessFile raf = new RandomAccessFile(asset, "r");
        raf.seek(from);

        final int len = to - from + 1;
        final MediaStreamer streamer = new MediaStreamer(len, raf);
        Response.ResponseBuilder res = Response.ok(streamer)
                .status(Response.Status.PARTIAL_CONTENT)
                .header("Accept-Ranges", "bytes")
                .header("Content-Range", responseRange)
                .header(HttpHeaders.CONTENT_LENGTH, streamer.getLenth())
                .header(HttpHeaders.LAST_MODIFIED, new Date(asset.lastModified()));

Setup

  • Clone this repository
  • mvn install

Testing

Run App in your favorite IDE.

Access the streaming resource

The media will be streamed at the following URL: http://localhost:9998/listen.

Request and responses logged for various browsers.

Chrome

ScreenShot

    Aug 06, 2017 1:04:49 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 2 * Server has received a request on thread qtp1374066265-18
    2 > GET http://localhost:9998/listen
    2 > Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    2 > Accept-Encoding: gzip, deflate, br
    2 > Accept-Language: en-US,en;q=0.8
    2 > Connection: keep-alive
    2 > Host: localhost:9998
    2 > Upgrade-Insecure-Requests: 1
    2 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
    
    Aug 06, 2017 1:04:49 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 2 * Server responded with a response on thread qtp1374066265-18
    2 < 200
    2 < Content-Length: 2836624
    2 < Content-Type: audio/mp3
    
    Aug 06, 2017 1:04:49 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 3 * Server has received a request on thread qtp1374066265-18
    3 > GET http://localhost:9998/listen
    3 > Accept: */*
    3 > Accept-Encoding: identity;q=1, *;q=0
    3 > Accept-Language: en-US,en;q=0.8
    3 > Connection: keep-alive
    3 > Host: localhost:9998
    3 > Range: bytes=0-
    3 > Referer: http://localhost:9998/listen
    3 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
    
    Aug 06, 2017 1:04:49 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 3 * Server responded with a response on thread qtp1374066265-18
    3 < 206
    3 < Accept-Ranges: bytes
    3 < Content-Length: 1048577
    3 < Content-Range: bytes 0-1048576/2836624
    3 < Content-Type: audio/mp3
    3 < Last-Modified: Sun, 06 Aug 2017 17:27:00 GMT
    
    Aug 06, 2017 1:04:50 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 4 * Server has received a request on thread qtp1374066265-13
    4 > GET http://localhost:9998/listen
    4 > Accept: */*
    4 > Accept-Encoding: identity;q=1, *;q=0
    4 > Accept-Language: en-US,en;q=0.8
    4 > Connection: keep-alive
    4 > Host: localhost:9998
    4 > If-Range: Sun, 06 Aug 2017 17:27:00 GMT
    4 > Range: bytes=1048577-2836623
    4 > Referer: http://localhost:9998/listen
    4 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
    
    Aug 06, 2017 1:04:50 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 4 * Server responded with a response on thread qtp1374066265-13
    4 < 206
    4 < Accept-Ranges: bytes
    4 < Content-Length: 1788047
    4 < Content-Range: bytes 1048577-2836623/2836624
    4 < Content-Type: audio/mp3
    4 < Last-Modified: Sun, 06 Aug 2017 17:27:00 GMT

Safari

    Aug 06, 2017 1:11:27 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 1 * Server has received a request on thread qtp1374066265-17
    1 > GET http://localhost:9998/listen
    1 > Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    1 > Accept-Encoding: gzip, deflate
    1 > Accept-Language: en-us
    1 > Connection: keep-alive
    1 > DNT: 1
    1 > Host: localhost:9998
    1 > Upgrade-Insecure-Requests: 1
    1 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8
    
    Aug 06, 2017 1:11:27 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 1 * Server responded with a response on thread qtp1374066265-17
    1 < 200
    1 < Content-Length: 2836624
    1 < Content-Type: audio/mp3
    
    Aug 06, 2017 1:11:27 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 2 * Server has received a request on thread qtp1374066265-11
    2 > GET http://localhost:9998/listen
    2 > Accept: */*
    2 > Accept-Encoding: identity
    2 > Accept-Language: en-us
    2 > Connection: keep-alive
    2 > DNT: 1
    2 > Host: localhost:9998
    2 > Range: bytes=0-1
    2 > Referer: http://localhost:9998/listen
    2 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8
    2 > X-Playback-Session-Id: 3330315E-8479-42C2-BFEF-36B0EA4BA735
    
    Aug 06, 2017 1:11:27 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 2 * Server responded with a response on thread qtp1374066265-11
    2 < 206
    2 < Accept-Ranges: bytes
    2 < Content-Length: 2
    2 < Content-Range: bytes 0-1/2836624
    2 < Content-Type: audio/mp3
    2 < Last-Modified: Sun, 06 Aug 2017 17:27:00 GMT
    
    Aug 06, 2017 1:11:27 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 3 * Server has received a request on thread qtp1374066265-17
    3 > GET http://localhost:9998/listen
    3 > Accept: */*
    3 > Accept-Encoding: identity
    3 > Accept-Language: en-us
    3 > Connection: keep-alive
    3 > DNT: 1
    3 > Host: localhost:9998
    3 > Range: bytes=0-2836623
    3 > Referer: http://localhost:9998/listen
    3 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8
    3 > X-Playback-Session-Id: 3330315E-8479-42C2-BFEF-36B0EA4BA735
    
    Aug 06, 2017 1:11:27 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 3 * Server responded with a response on thread qtp1374066265-17
    3 < 206
    3 < Accept-Ranges: bytes
    3 < Content-Length: 2836624
    3 < Content-Range: bytes 0-2836623/2836624
    3 < Content-Type: audio/mp3
    3 < Last-Modified: Sun, 06 Aug 2017 17:27:00 GMT
    
    Aug 06, 2017 1:11:27 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 4 * Server has received a request on thread qtp1374066265-13
    4 > GET http://localhost:9998/listen
    4 > Accept: */*
    4 > Accept-Encoding: identity
    4 > Accept-Language: en-us
    4 > Connection: keep-alive
    4 > DNT: 1
    4 > Host: localhost:9998
    4 > Range: bytes=131072-2836623
    4 > Referer: http://localhost:9998/listen
    4 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8
    4 > X-Playback-Session-Id: 3330315E-8479-42C2-BFEF-36B0EA4BA735
    
    Aug 06, 2017 1:11:27 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 4 * Server responded with a response on thread qtp1374066265-13
    4 < 206
    4 < Accept-Ranges: bytes
    4 < Content-Length: 2705552
    4 < Content-Range: bytes 131072-2836623/2836624
    4 < Content-Type: audio/mp3
    4 < Last-Modified: Sun, 06 Aug 2017 17:27:00 GMT

Firefox

    Aug 06, 2017 1:13:22 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 1 * Server has received a request on thread qtp1374066265-17
    1 > GET http://localhost:9998/listen
    1 > Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    1 > Accept-Encoding: gzip, deflate
    1 > Accept-Language: en-US,en;q=0.5
    1 > Connection: keep-alive
    1 > DNT: 1
    1 > Host: localhost:9998
    1 > Upgrade-Insecure-Requests: 1
    1 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0
    
    Aug 06, 2017 1:13:22 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 1 * Server responded with a response on thread qtp1374066265-17
    1 < 200
    1 < Content-Length: 2836624
    1 < Content-Type: audio/mp3

Edge

    Aug 06, 2017 2:56:03 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 1 * Server has received a request on thread qtp1374066265-17
    1 > GET http://localhost:9998/listen
    1 > Accept: text/html, application/xhtml+xml, image/jxr, */*
    1 > Accept-Encoding: gzip, deflate
    1 > Accept-Language: en-US
    1 > Connection: keep-alive
    1 > DNT: 1
    1 > Host: localhost:9998
    1 > User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063
    
    Aug 06, 2017 2:56:03 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 1 * Server responded with a response on thread qtp1374066265-17
    1 < 200
    1 < Content-Length: 2836624
    1 < Content-Type: audio/mp3
    
    Aug 06, 2017 2:56:04 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 2 * Server has received a request on thread qtp1374066265-16
    2 > GET http://localhost:9998/listen
    2 > Accept: */*
    2 > Accept-Encoding: gzip, deflate
    2 > Accept-Language: en-US
    2 > Cache-Control: no-cache
    2 > Connection: keep-alive
    2 > DNT: 1
    2 > GetContentFeatures.DLNA.ORG: 1
    2 > Host: localhost:9998
    2 > If-Unmodified-Since: Sun, 06 Aug 2017 17:27:00 GMT
    2 > Range: bytes=1048577-2836623
    2 > User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063
    
    Aug 06, 2017 2:56:04 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 2 * Server responded with a response on thread qtp1374066265-16
    2 < 206
    2 < Accept-Ranges: bytes
    2 < Content-Length: 1788047
    2 < Content-Range: bytes 1048577-2836623/2836624
    2 < Content-Type: audio/mp3
    2 < Last-Modified: Sun, 06 Aug 2017 17:27:00 GMT

Opera

    Aug 06, 2017 1:25:50 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 1 * Server has received a request on thread qtp1374066265-18
    1 > GET http://localhost:9998/listen
    1 > Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    1 > Accept-Encoding: gzip, deflate, br
    1 > Accept-Language: en-US,en;q=0.8
    1 > Connection: keep-alive
    1 > Host: localhost:9998
    1 > Upgrade-Insecure-Requests: 1
    1 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2597.57
    
    Aug 06, 2017 1:25:50 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 1 * Server responded with a response on thread qtp1374066265-18
    1 < 200
    1 < Content-Length: 2836624
    1 < Content-Type: audio/mp3
    
    Aug 06, 2017 1:25:50 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 2 * Server has received a request on thread qtp1374066265-11
    2 > GET http://localhost:9998/listen
    2 > Accept: */*
    2 > Accept-Encoding: identity;q=1, *;q=0
    2 > Accept-Language: en-US,en;q=0.8
    2 > Connection: keep-alive
    2 > Host: localhost:9998
    2 > Range: bytes=0-
    2 > Referer: http://localhost:9998/listen
    2 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2597.57
    
    Aug 06, 2017 1:25:50 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 2 * Server responded with a response on thread qtp1374066265-11
    2 < 206
    2 < Accept-Ranges: bytes
    2 < Content-Length: 1048577
    2 < Content-Range: bytes 0-1048576/2836624
    2 < Content-Type: audio/mp3
    2 < Last-Modified: Sun, 06 Aug 2017 17:27:00 GMT
    
    Aug 06, 2017 1:25:50 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 3 * Server has received a request on thread qtp1374066265-16
    3 > GET http://localhost:9998/listen
    3 > Accept: */*
    3 > Accept-Encoding: identity;q=1, *;q=0
    3 > Accept-Language: en-US,en;q=0.8
    3 > Connection: keep-alive
    3 > Host: localhost:9998
    3 > If-Range: Sun, 06 Aug 2017 17:27:00 GMT
    3 > Range: bytes=1048577-2836623
    3 > Referer: http://localhost:9998/listen
    3 > User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2597.57
    
    Aug 06, 2017 1:25:50 PM org.glassfish.jersey.logging.LoggingInterceptor log
    INFO: 3 * Server responded with a response on thread qtp1374066265-16
    3 < 206
    3 < Accept-Ranges: bytes
    3 < Content-Length: 1788047
    3 < Content-Range: bytes 1048577-2836623/2836624
    3 < Content-Type: audio/mp3
    3 < Last-Modified: Sun, 06 Aug 2017 17:27:00 GMT

Mp3 Credit

http://publicdomain4u.com/scott-joplin-the-entertainer/mp3-download

jersey-streaming's People

Contributors

floifyarul avatar aruld avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.