2 Replies Latest reply on Apr 5, 2010 9:16 AM by Michael Ressler

    Supporting byte-ranges inside a Seam component

    Benjamin Goll Newbie


      I currently try to implement a simple streaming-server for ogg vorbis-files. This server will be included into a bigger project. The ogg-files will be played back via the HTML5 audio-tag in Firefox. Serving the files to the browser works fine but not seeking inside of the file. For this to work properly, I would have to support byte-ranges inside my Seam component (according to http://pearce.org.nz/blog/?p=9). Since Tomcat supports byte-ranges by default, I would like to pass my outputstream directly to Tomcat. Is there any possibility for this?
      Maybe a bit of code can make more clear what I mean:

      public class StreamingServer
              private Log log;
              EntityManager entityManager;
              String libraryPath;
              private ExternalContext extCtx;
              FacesContext facesContext;
              private Long trackId;
              private static final int DEFAULT_BUFFER_SIZE = 1024;
              public void download()
                      Track track = entityManager.find(Track.class, trackId);
                      File fileHandle = new File(libraryPath + track.getFilePath());
                      HttpServletResponse response = (HttpServletResponse) extCtx.getResponse();
                      response.setContentLength((int) fileHandle.length());
                      response.setHeader("X-Content-Duration", Float.toString(track.getLength()));
                      BufferedInputStream in = null;
                      BufferedOutputStream out = null;
                              in = new BufferedInputStream(new FileInputStream(fileHandle), DEFAULT_BUFFER_SIZE);
                              out = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);
                              byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
                              int length;
                              while ((length = in.read(buffer)) > 0)
                                   out.write(buffer, 0, length);
                      catch(Exception e)
                              log.error("Failure : " + e.toString());

      What I would like to do is read in the ogg-file from the disk and pass it to Tomcat's default servlet which could then serve it supporting byte-ranges.
      If this is not possible, do you see any other possibility to achieve my goal?

      JBoss 4.2.3GA
      Seam 2.2.0GA
      ICEFaces 1.8.2

      Thanks in advance!



        • 1. Re: Supporting byte-ranges inside a Seam component
          Michael Ressler Newbie


          I was struggling with a similar problem with OGG video within Tomcat.  My solution at the moment is to create a new Tomcat Context for serving all of my video related content.  This lets Tomcat do its Byte-Ranges business, but unfortunately it doesn't help me out with the response header X-Content-Duration like I'd like it to (I can, however, modify the mime-type mapping through Tomcat's web.xml under the conf directory).  I would like to point out that the browsers I've tested this in do a fine job of dealing without the X-Content-Duration header set and even dealing without the mime-type set in some cases.  The MDC has a good write-up of how to be a good HTML5 content-serving citizen here.  The only downside to not serving up the X-Content-Duration header seem to be a few more HTTP requests (with byte-ranges) to seek to the end of the file to determine the duration and then back to the beginning of the requested content.

          I came across your question while looking for a solution to serving up OGG content in Tomcat with X-Content-Duration headers set.  My current thought is to play with Tomcat Filters.  It seems like a Filter would be able to intercept the request at the right point and muck with the headers before returning the response (and still allowing for the byte-ranges goodness).

          I'll report my findings here since this post ranks well in Tomcat 'x-content-duration' searches :)

          Mike Ressler

          • 2. Re: Supporting byte-ranges inside a Seam component
            Michael Ressler Newbie

            Adding a Filter that adds the X-Content-Duration header works like a charm.  The catch now is to cache the duration information somewhere sensible to be retrieved between server starts and stops.

            I used the Filter example from here and at the moment I'm hard-coding the duration.  This brought the number of requests for the one particular video down from 5 requests to 1!  Not very noticeable when it's hosted on my machine, but I'm sure that will help users streaming video online.

            For fun, here's my ContentDurationFilter:

            import java.io.IOException;
            import javax.servlet.Filter;
            import javax.servlet.FilterChain;
            import javax.servlet.FilterConfig;
            import javax.servlet.ServletException;
            import javax.servlet.ServletRequest;
            import javax.servlet.ServletResponse;
            import javax.servlet.http.HttpServletRequest;
            import javax.servlet.http.HttpServletResponse;
            public class ContentDurationFilter implements Filter {
                 private FilterConfig filterConfig = null;
                 public void init(FilterConfig filterConfig) throws ServletException {
                      // apparently I need this?
                      this.filterConfig = filterConfig;
                 public void destroy() {
                      this.filterConfig = null;
                 public void doFilter(
                           ServletRequest request, 
                           ServletResponse response,
                           FilterChain chain) 
                      throws IOException, ServletException 
                      System.out.println("Filter ran on " + ((HttpServletRequest)request).getRequestURI());
                      // TODO: Not the laziest thing you could think of.  Actually do some intelligent caching of this data.
                      ((HttpServletResponse)response).addHeader("X-Content-Duration", "1178.818726");
                      chain.doFilter(request, response);

            The rest (byte-ranges, mime type, content length) is all handled by Tomcat behind the scenes.  For my particular video application, I use the ffmpeg2theora library wrapped in a slightly modified JAVE layer for conversion and introspection of the videos.

            Hope this helps other folks!