You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by mg...@apache.org on 2015/03/30 21:01:28 UTC

[2/4] wicket git commit: WICKET-5819 Support for HTML 5 media tags (audio / video)

WICKET-5819 Support for HTML 5 media tags (audio / video)

Move range handling code to AbstractResource.
Remove MediaStreamingResourceReference.


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/f623ceaa
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/f623ceaa
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/f623ceaa

Branch: refs/heads/master
Commit: f623ceaa93696427aafa8a15ecc86f2d0e362969
Parents: 499f11f
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Wed Mar 25 23:29:51 2015 +0200
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Mon Mar 30 21:59:14 2015 +0300

----------------------------------------------------------------------
 .../markup/html/media/MediaComponent.java       |  29 +-
 .../media/MediaStreamingResourceReference.java  | 182 ------------
 .../markup/html/media/PartWriterCallback.java   | 185 ------------
 .../apache/wicket/markup/html/media/Source.java |  36 ++-
 .../wicket/markup/html/media/audio/Audio.java   |  18 +-
 .../wicket/markup/html/media/video/Video.java   |  18 +-
 .../request/resource/AbstractResource.java      | 280 +++++++++++++++++--
 .../request/resource/PackageResource.java       |  90 +++---
 .../request/resource/PartWriterCallback.java    | 184 ++++++++++++
 .../html/media/MediaTagsExtendedTestPage.java   |   9 +-
 .../markup/html/media/MediaTagsTestPage.java    |   8 +-
 .../org/apache/wicket/examples/media/Home.java  |   7 +-
 .../apache/wicket/request/http/WebResponse.java |  52 +++-
 13 files changed, 583 insertions(+), 515 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java
index 093cd8e..83186f9 100755
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java
@@ -23,6 +23,7 @@ import org.apache.wicket.request.Url;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
+import org.apache.wicket.request.resource.PackageResourceReference;
 
 /**
  * The media component is used to provide basic functionality to the video and audio component. The
@@ -100,7 +101,7 @@ public abstract class MediaComponent extends WebMarkupContainer
 
 	private final PageParameters pageParameters;
 
-	private final MediaStreamingResourceReference mediaStreamingResourceReference;
+	private final PackageResourceReference resourceReference;
 
 	private final String url;
 
@@ -133,31 +134,31 @@ public abstract class MediaComponent extends WebMarkupContainer
 	 *
 	 * @param id
 	 *            The component id
-	 * @param mediaStreamingResourceReference
+	 * @param resourceReference
 	 */
-	public MediaComponent(String id, MediaStreamingResourceReference mediaStreamingResourceReference)
+	public MediaComponent(String id, PackageResourceReference resourceReference)
 	{
-		this(id, null, null, null, mediaStreamingResourceReference);
+		this(id, null, null, null, resourceReference);
 	}
 
 	public MediaComponent(String id, IModel<?> model,
-		MediaStreamingResourceReference mediaStreamingResourceReference)
+		PackageResourceReference resourceReference)
 	{
-		this(id, model, null, null, mediaStreamingResourceReference);
+		this(id, model, null, null, resourceReference);
 	}
 
 	public MediaComponent(String id,
-		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PackageResourceReference resourceReference,
 		PageParameters pageParameters)
 	{
-		this(id, null, null, pageParameters, mediaStreamingResourceReference);
+		this(id, null, null, pageParameters, resourceReference);
 	}
 
 	public MediaComponent(String id, IModel<?> model,
-		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PackageResourceReference resourceReference,
 		PageParameters pageParameters)
 	{
-		this(id, model, null, pageParameters, mediaStreamingResourceReference);
+		this(id, model, null, pageParameters, resourceReference);
 	}
 
 	public MediaComponent(String id, String url)
@@ -176,12 +177,12 @@ public abstract class MediaComponent extends WebMarkupContainer
 	}
 
 	private MediaComponent(String id, IModel<?> model, String url, PageParameters pageParameters,
-		MediaStreamingResourceReference mediaStreamingResourceReference)
+		PackageResourceReference resourceReference)
 	{
 		super(id, model);
 		this.url = url;
 		this.pageParameters = pageParameters;
-		this.mediaStreamingResourceReference = mediaStreamingResourceReference;
+		this.resourceReference = resourceReference;
 	}
 
 	@Override
@@ -197,10 +198,10 @@ public abstract class MediaComponent extends WebMarkupContainer
 			timeManagement += "#t=" + startTime + (endTime != null ? "," + endTime : "");
 		}
 
-		if (mediaStreamingResourceReference != null)
+		if (resourceReference != null)
 		{
 			CharSequence urlToMediaReference = RequestCycle.get().urlFor(
-				mediaStreamingResourceReference, pageParameters);
+					resourceReference, pageParameters);
 			tag.put("src", urlToMediaReference + timeManagement);
 		}
 		else if (url != null)

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaStreamingResourceReference.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaStreamingResourceReference.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaStreamingResourceReference.java
deleted file mode 100755
index 76a208f..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaStreamingResourceReference.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.markup.html.media;
-
-import java.io.IOException;
-import java.util.Locale;
-
-import org.apache.wicket.Application;
-import org.apache.wicket.WicketRuntimeException;
-import org.apache.wicket.request.Request;
-import org.apache.wicket.request.Response;
-import org.apache.wicket.request.http.WebRequest;
-import org.apache.wicket.request.http.WebResponse;
-import org.apache.wicket.request.resource.ContentDisposition;
-import org.apache.wicket.request.resource.PackageResource;
-import org.apache.wicket.request.resource.PackageResourceReference;
-import org.apache.wicket.util.resource.IResourceStream;
-import org.apache.wicket.util.string.Strings;
-
-/**
- * The media streaming resource reference is used to provided streamed data based on bytes requested
- * by the client for video and audio files
- *
- * @author Tobias Soloschenko
- */
-public class MediaStreamingResourceReference extends PackageResourceReference
-{
-	private static final long serialVersionUID = 1L;
-
-	public MediaStreamingResourceReference(Class<?> scope, String name, Locale locale,
-		String style, String variation)
-	{
-		super(scope, name, locale, style, variation);
-	}
-
-	public MediaStreamingResourceReference(Class<?> scope, String name)
-	{
-		this(scope, name, null, null, null);
-	}
-
-	public MediaStreamingResourceReference(Key key)
-	{
-		super(key);
-	}
-
-	public MediaStreamingResourceReference(String name)
-	{
-		super(name);
-	}
-
-	@Override
-	public PackageResource getResource()
-	{
-		return new PackageResource(getScope(), getName(), getLocale(), getStyle(), getVariation())
-		{
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			protected ResourceResponse newResourceResponse(Attributes attributes)
-			{
-				IResourceStream resourceStream = getResourceStream();
-				if (resourceStream == null)
-				{
-					throw new WicketRuntimeException("Cannot find resource: " + toString());
-				}
-				try
-				{
-					Request request = attributes.getRequest();
-					Response response = attributes.getResponse();
-
-					if (!(request instanceof WebRequest) || !(response instanceof WebResponse))
-					{
-						throw new IllegalStateException(
-							"Web request/response are required! Request: " + request +
-								", response: " + response);
-					}
-
-					WebRequest webRequest = (WebRequest)request;
-					WebResponse webResponse = (WebResponse)response;
-
-					long length = resourceStream.length().bytes();
-
-					ResourceResponse resourceResponse = new ResourceResponse();
-					resourceResponse.setContentType(resourceStream.getContentType());
-					resourceResponse.setFileName(MediaStreamingResourceReference.this.getName());
-					resourceResponse.setContentDisposition(ContentDisposition.ATTACHMENT);
-					resourceResponse.setLastModified(resourceStream.lastModifiedTime());
-
-					// accept ranges, so that the player can
-					// load and play content from a specific byte position
-					webResponse.setHeader("Accept-Range", "bytes");
-
-					Long startbyte = null;
-					Long endbyte = null;
-
-					// Calculating the response code and the byte range to be played
-					String rangeHeader = webRequest.getHeader("range");
-					if (Strings.isEmpty(rangeHeader))
-					{
-						resourceResponse.setStatusCode(200);
-						resourceResponse.setContentLength(length);
-					}
-					else
-					{
-						rangeHeader = rangeHeader.replaceAll(" ", "");
-						// partial content has to be returned
-						resourceResponse.setStatusCode(206);
-
-						// And now the calculation of the range to be read
-						// and to be given as response within the Content-Range header
-						// for more information take a look here:
-						// http://stackoverflow.com/questions/8293687/sample-http-range-request-session
-						String range = rangeHeader.substring(rangeHeader.indexOf('=') + 1,
-							rangeHeader.length());
-						String[] rangeParts = Strings.split(range, '-');
-
-						String startByteString = rangeParts[0];
-						String endByteString = rangeParts[1];
-
-						startbyte = startByteString != null && !startByteString.trim().equals("")
-							? Long.parseLong(startByteString) : 0;
-						endbyte = endByteString != null && !endByteString.trim().equals("")
-							? Long.parseLong(endByteString) : length - 1;
-
-						webResponse.setHeader("Content-Range", "bytes " + startbyte + '-' +
-							endbyte + '/' + length);
-						resourceResponse.setContentLength((endbyte - startbyte) + 1);
-					}
-
-					// Apply the writer callback to send the requested part to the client
-					resourceResponse.setWriteCallback(new PartWriterCallback(resourceStream,
-						startbyte, endbyte));
-
-					return resourceResponse;
-				}
-				catch (Exception e)
-				{
-					throw new WicketRuntimeException(
-						"A problem occurred while creating the video response.", e);
-				}
-				finally
-				{
-					try
-					{
-						resourceStream.close();
-					}
-					catch (IOException e)
-					{
-						throw new WicketRuntimeException(
-							"A problem occurred while closing the video response stream.", e);
-					}
-				}
-			}
-		};
-
-	}
-
-	/**
-	 * Returns the mime type of the media this resource reference belongs to
-	 *
-	 * @return the mime type of this media
-	 */
-	public String getType()
-	{
-		final String resourceName = MediaStreamingResourceReference.this.getName();
-		return Application.get().getMimeType(resourceName);
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/PartWriterCallback.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/PartWriterCallback.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/PartWriterCallback.java
deleted file mode 100644
index da8abda..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/PartWriterCallback.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.markup.html.media;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import org.apache.wicket.WicketRuntimeException;
-import org.apache.wicket.protocol.http.servlet.ResponseIOException;
-import org.apache.wicket.request.resource.AbstractResource.WriteCallback;
-import org.apache.wicket.request.resource.IResource.Attributes;
-import org.apache.wicket.util.io.Streams;
-import org.apache.wicket.util.resource.IResourceStream;
-
-/**
- * Used to read a part of the package resource stream and write it to the output stream of the
- * response.
- *
- * @author Tobias Soloschenko
- *
- */
-public class PartWriterCallback extends WriteCallback
-{
-	private final IResourceStream resourceStream;
-
-	private final Long startbyte;
-
-	private Long endbyte;
-
-	private int bufferSize;
-
-	/**
-	 * Creates a part writer callback.<br>
-	 * <br>
-	 * Reads a part of the given resource stream. If the startbyte parameter is not null the
-	 * number of bytes are skipped till the stream is read. If the endbyte is not null the stream is
-	 * read till endbyte, else to the end of the whole stream. If startbyte and endbyte is null the
-	 * whole stream is read.
-	 *
-	 * @param resourceStream
-	 *            the resource stream to read
-	 * @param startbyte
-	 *            the start position to read from (if not null the number of bytes are skipped till
-	 *            the stream is read)
-	 * @param endbyte
-	 *            the end position to read to (if not null the stream is going to be read till
-	 *            endbyte, else to the end of the whole stream)
-	 */
-	public PartWriterCallback(IResourceStream resourceStream, Long startbyte,
-		Long endbyte)
-	{
-		this.resourceStream = resourceStream;
-		this.startbyte = startbyte;
-		this.endbyte = endbyte;
-	}
-
-	/**
-	 * Writes the data
-	 *
-	 * @param attributes
-	 *            the attributes to get the output stream of the response
-	 */
-	@Override
-	public void writeData(Attributes attributes) throws IOException
-	{
-		try
-		{
-			InputStream inputStream = resourceStream.getInputStream();
-			OutputStream outputStream = attributes.getResponse().getOutputStream();
-			byte[] buffer = new byte[getBufferSize()];
-
-			if (startbyte != null || endbyte != null)
-			{
-				// skipping the first bytes which are
-				// requested to be skipped by the client
-				if (startbyte != null)
-				{
-					inputStream.skip(startbyte);
-				}
-
-				// If there are no end bytes given read the whole stream till the end
-				if (endbyte == null)
-				{
-					endbyte = resourceStream.length().bytes();
-				}
-
-				long totalBytes = 0;
-				int actualReadBytes;
-
-				while ((actualReadBytes = inputStream.read(buffer)) != -1)
-				{
-					totalBytes = totalBytes + buffer.length;
-					long lowerBuffer = endbyte - totalBytes;
-					if (lowerBuffer <= 0)
-					{
-						buffer = resizeArray(buffer, actualReadBytes);
-						outputStream.write(buffer);
-						break;
-					}
-					else
-					{
-						outputStream.write(buffer);
-					}
-				}
-			}
-			else
-			{
-				Streams.copy(inputStream, outputStream, getBufferSize());
-			}
-		}
-		catch (ResponseIOException e)
-		{
-			// the client has closed the connection and
-			// doesn't read the stream further on
-			// (in tomcats
-			// org.apache.catalina.connector.ClientAbortException)
-			// we ignore this case
-		}
-		catch (Exception e)
-		{
-			throw new WicketRuntimeException(
-				"A problem occurred while writing the buffer to the output stream.", e);
-		}
-	}
-
-	/**
-	 * Reallocates an array with a new size, and copies the contents of the old array to the new
-	 * array.
-	 *
-	 * @param oldArray
-	 *            the old array, to be reallocated.
-	 * @param newSize
-	 *            the new array size.
-	 * @return A new array with the same contents.
-	 */
-	@SuppressWarnings("rawtypes")
-	private static byte[] resizeArray(byte[] oldArray, int newSize)
-	{
-		int oldSize = oldArray.length;
-		byte[] newArray = new byte[newSize];
-		int minLength = Math.min(oldSize, newSize);
-		if (minLength > 0)
-		{
-			System.arraycopy(oldArray, 0, newArray, 0, minLength);
-		}
-		return newArray;
-	}
-
-	/**
-	 * Sets the buffer size used to send the data to the client
-	 *
-	 * @return the buffer size used to send the data to the client (default is 4096)
-	 */
-	public int getBufferSize()
-	{
-		return bufferSize > 0 ? bufferSize : 4096;
-	}
-
-	/**
-	 * Sets the buffer size used to send the data to the client
-	 *
-	 * @param bufferSize
-	 *            the buffer size used to send the data to the client
-	 */
-	public void setBufferSize(int bufferSize)
-	{
-		this.bufferSize = bufferSize;
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java
index 76b4002..6b19e60 100755
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java
@@ -21,6 +21,9 @@ import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.PackageResource;
+import org.apache.wicket.request.resource.PackageResourceReference;
+import org.apache.wicket.util.resource.IResourceStream;
 
 /**
  * The source of an audio or a video media component
@@ -38,7 +41,7 @@ public class Source extends WebMarkupContainer
 
 	private String media;
 
-	private final MediaStreamingResourceReference mediaStreamingResourceReference;
+	private final PackageResourceReference resourceReference;
 
 	private final PageParameters pageParameters;
 
@@ -54,28 +57,28 @@ public class Source extends WebMarkupContainer
 		this(id, model, null, null, null);
 	}
 
-	public Source(String id, MediaStreamingResourceReference mediaStreamingResourceReference)
+	public Source(String id, PackageResourceReference resourceReference)
 	{
-		this(id, null, null, null, mediaStreamingResourceReference);
+		this(id, null, null, null, resourceReference);
 	}
 
 	public Source(String id, IModel<?> model,
-		MediaStreamingResourceReference mediaStreamingResourceReference)
+		PackageResourceReference resourceReference)
 	{
-		this(id, model, null, null, mediaStreamingResourceReference);
+		this(id, model, null, null, resourceReference);
 	}
 
-	public Source(String id, MediaStreamingResourceReference mediaStreamingResourceReference,
+	public Source(String id, PackageResourceReference resourceReference,
 		PageParameters pageParameters)
 	{
-		this(id, null, null, pageParameters, mediaStreamingResourceReference);
+		this(id, null, null, pageParameters, resourceReference);
 	}
 
 	public Source(String id, IModel<?> model,
-		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PackageResourceReference resourceReference,
 		PageParameters pageParameters)
 	{
-		this(id, model, null, pageParameters, mediaStreamingResourceReference);
+		this(id, model, null, pageParameters, resourceReference);
 	}
 
 	public Source(String id, String url)
@@ -89,12 +92,12 @@ public class Source extends WebMarkupContainer
 	}
 
 	private Source(String id, IModel<?> model, String url, PageParameters pageParameters,
-	               MediaStreamingResourceReference mediaStreamingResourceReference)
+	               PackageResourceReference resourceReference)
 	{
 		super(id, model);
 		this.url = url;
 		this.pageParameters = pageParameters;
-		this.mediaStreamingResourceReference = mediaStreamingResourceReference;
+		this.resourceReference = resourceReference;
 	}
 
 	@Override
@@ -103,9 +106,9 @@ public class Source extends WebMarkupContainer
 		checkComponentTag(tag, "source");
 		super.onComponentTag(tag);
 
-		if (mediaStreamingResourceReference != null)
+		if (resourceReference != null)
 		{
-			CharSequence url = RequestCycle.get().urlFor(mediaStreamingResourceReference, pageParameters);
+			CharSequence url = RequestCycle.get().urlFor(resourceReference, pageParameters);
 			tag.put("src", url);
 		} else if (url != null)
 		{
@@ -118,9 +121,12 @@ public class Source extends WebMarkupContainer
 			{
 				tag.put("type", type);
 			}
-			else if (mediaStreamingResourceReference != null)
+			else if (resourceReference != null)
 			{
-				tag.put("type", mediaStreamingResourceReference.getType());
+				PackageResource resource = resourceReference.getResource();
+				IResourceStream resourceStream = resource.getResourceStream();
+				String contentType = resourceStream.getContentType();
+				tag.put("type", contentType);
 			}
 		}
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java
index e16cb8b..c0592fa 100755
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java
@@ -18,9 +18,9 @@ package org.apache.wicket.markup.html.media.audio;
 
 import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.html.media.MediaComponent;
-import org.apache.wicket.markup.html.media.MediaStreamingResourceReference;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.PackageResourceReference;
 
 /**
  * An audio media component to playback audio files.
@@ -42,28 +42,28 @@ public class Audio extends MediaComponent
 		super(id, model);
 	}
 
-	public Audio(String id, MediaStreamingResourceReference mediaStreamingResourceReference)
+	public Audio(String id, PackageResourceReference resourceReference)
 	{
-		super(id, mediaStreamingResourceReference);
+		super(id, resourceReference);
 	}
 
 	public Audio(String id, IModel<?> model,
-		MediaStreamingResourceReference mediaStreamingResourceReference)
+		PackageResourceReference resourceReference)
 	{
-		super(id, model, mediaStreamingResourceReference);
+		super(id, model, resourceReference);
 	}
 
-	public Audio(String id, MediaStreamingResourceReference mediaStreamingResourceReference,
+	public Audio(String id, PackageResourceReference resourceReference,
 		PageParameters pageParameters)
 	{
-		super(id, mediaStreamingResourceReference, pageParameters);
+		super(id, resourceReference, pageParameters);
 	}
 
 	public Audio(String id, IModel<?> model,
-		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PackageResourceReference resourceReference,
 		PageParameters pageParameters)
 	{
-		super(id, model, mediaStreamingResourceReference, pageParameters);
+		super(id, model, resourceReference, pageParameters);
 	}
 
 	public Audio(String id, String url)

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java
index b73ba3d..1106696 100755
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java
@@ -18,10 +18,10 @@ package org.apache.wicket.markup.html.media.video;
 
 import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.html.media.MediaComponent;
-import org.apache.wicket.markup.html.media.MediaStreamingResourceReference;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.PackageResourceReference;
 import org.apache.wicket.request.resource.ResourceReference;
 
 /**
@@ -52,28 +52,28 @@ public class Video extends MediaComponent
 		super(id, model);
 	}
 
-	public Video(String id, MediaStreamingResourceReference mediaStreamingResourceReference)
+	public Video(String id, PackageResourceReference resourceReference)
 	{
-		super(id, mediaStreamingResourceReference);
+		super(id, resourceReference);
 	}
 
 	public Video(String id, IModel<?> model,
-		MediaStreamingResourceReference mediaStreamingResourceReference)
+		PackageResourceReference resourceReference)
 	{
-		super(id, model, mediaStreamingResourceReference);
+		super(id, model, resourceReference);
 	}
 
-	public Video(String id, MediaStreamingResourceReference mediaStreamingResourceReference,
+	public Video(String id, PackageResourceReference resourceReference,
 		PageParameters pageParameters)
 	{
-		super(id, mediaStreamingResourceReference, pageParameters);
+		super(id, resourceReference, pageParameters);
 	}
 
 	public Video(String id, IModel<?> model,
-		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PackageResourceReference resourceReference,
 		PageParameters pageParameters)
 	{
-		super(id, model, mediaStreamingResourceReference, pageParameters);
+		super(id, model, resourceReference, pageParameters);
 	}
 
 	public Video(String id, String url)

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java
index 386b7b3..18764d4 100644
--- a/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java
@@ -24,9 +24,12 @@ import java.util.Set;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.wicket.Application;
+import org.apache.wicket.MetaDataKey;
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.request.HttpHeaderCollection;
+import org.apache.wicket.request.Request;
 import org.apache.wicket.request.Response;
+import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.http.WebRequest;
 import org.apache.wicket.request.http.WebResponse;
 import org.apache.wicket.request.resource.caching.IResourceCachingStrategy;
@@ -34,6 +37,7 @@ import org.apache.wicket.request.resource.caching.IStaticCacheableResource;
 import org.apache.wicket.util.io.Streams;
 import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.lang.Classes;
+import org.apache.wicket.util.string.Strings;
 import org.apache.wicket.util.time.Duration;
 import org.apache.wicket.util.time.Time;
 
@@ -50,6 +54,39 @@ public abstract class AbstractResource implements IResource
 	/** header values that are managed internally and must not be set directly */
 	public static final Set<String> INTERNAL_HEADERS;
 
+	/** The meta data key of the content range start byte **/
+	public static final MetaDataKey<Long> CONTENT_RANGE_STARTBYTE = new MetaDataKey<Long>()
+	{
+		private static final long serialVersionUID = 1L;
+	};
+
+	/** The meta data key of the content range end byte **/
+	public static final MetaDataKey<Long> CONTENT_RANGE_ENDBYTE = new MetaDataKey<Long>()
+	{
+		private static final long serialVersionUID = 1L;
+	};
+
+	/**
+	 * All available content range types. The type name represents the name used in header
+	 * information.
+	 */
+	public enum ContentRangeType
+	{
+		BYTES("bytes"), NONE("none");
+
+		private final String typeName;
+
+		private ContentRangeType(String typeName)
+		{
+			this.typeName = typeName;
+		}
+
+		public String getTypeName()
+		{
+			return typeName;
+		}
+	}
+
 	static
 	{
 		INTERNAL_HEADERS = new HashSet<>();
@@ -63,6 +100,8 @@ public abstract class AbstractResource implements IResource
 		INTERNAL_HEADERS.add("transfer-encoding");
 		INTERNAL_HEADERS.add("connection");
 		INTERNAL_HEADERS.add("content-disposition");
+		INTERNAL_HEADERS.add("content-range");
+		INTERNAL_HEADERS.add("accept-range");
 	}
 
 	/**
@@ -94,6 +133,8 @@ public abstract class AbstractResource implements IResource
 		private String fileName = null;
 		private ContentDisposition contentDisposition = ContentDisposition.INLINE;
 		private String contentType = null;
+		private String contentRange = null;
+		private ContentRangeType contentRangeType = null;
 		private String textEncoding;
 		private long contentLength = -1;
 		private Time lastModified = null;
@@ -262,8 +303,53 @@ public abstract class AbstractResource implements IResource
 		}
 
 		/**
-		 * Sets the text encoding for the resource. This setting must only used 
-		 * if the resource response represents text.
+		 * Gets the content range of the resource. If no content range is set the client assumes the
+		 * whole content.
+		 *
+		 * @return the content range
+		 */
+		public String getContentRange()
+		{
+			return contentRange;
+		}
+
+		/**
+		 * Sets the content range of the resource. If no content range is set the client assumes the
+		 * whole content. Please note that if the content range is set, the content length, the
+		 * status code and the accept range must be set right, too.
+		 *
+		 * @param contentRange
+		 *            the content range
+		 */
+		public void setContentRange(String contentRange)
+		{
+			this.contentRange = contentRange;
+		}
+
+		/**
+		 * If the resource accepts ranges
+		 *
+		 * @return the type of range (e.g. bytes)
+		 */
+		public ContentRangeType getAcceptRange()
+		{
+			return contentRangeType;
+		}
+
+		/**
+		 * Sets the accept range header (e.g. bytes)
+		 *
+		 * @param contentRangeType
+		 *            the content range header information
+		 */
+		public void setAcceptRange(ContentRangeType contentRangeType)
+		{
+			this.contentRangeType = contentRangeType;
+		}
+
+		/**
+		 * Sets the text encoding for the resource. This setting must only used if the resource
+		 * response represents text.
 		 * 
 		 * @param textEncoding
 		 *            character encoding of text body
@@ -534,6 +620,9 @@ public abstract class AbstractResource implements IResource
 	@Override
 	public void respond(final Attributes attributes)
 	{
+		// Sets the request attributes
+		setRequestMetaData(attributes);
+
 		// Get a "new" ResourceResponse to write a response
 		ResourceResponse data = newResourceResponse(attributes);
 
@@ -552,8 +641,8 @@ public abstract class AbstractResource implements IResource
 		// set response header
 		setResponseHeaders(data, attributes);
 
-		if (!data.dataNeedsToBeWritten(attributes) || data.getErrorCode() != null
-				|| needsBody(data.getStatusCode()) == false)
+		if (!data.dataNeedsToBeWritten(attributes) || data.getErrorCode() != null ||
+			needsBody(data.getStatusCode()) == false)
 		{
 			return;
 		}
@@ -574,19 +663,19 @@ public abstract class AbstractResource implements IResource
 	}
 
 	/**
-	 * Decides whether a response body should be written back to the client depending
-	 * on the set status code
+	 * Decides whether a response body should be written back to the client depending on the set
+	 * status code
 	 *
 	 * @param statusCode
-	 *      the status code set by the application
+	 *            the status code set by the application
 	 * @return {@code true} if the status code allows response body, {@code false} - otherwise
 	 */
 	private boolean needsBody(Integer statusCode)
 	{
 		return statusCode == null ||
-					(statusCode < 300 &&
-					statusCode != HttpServletResponse.SC_NO_CONTENT &&
-					statusCode != HttpServletResponse.SC_RESET_CONTENT);
+								(statusCode < 300 &&
+								statusCode != HttpServletResponse.SC_NO_CONTENT &&
+								statusCode != HttpServletResponse.SC_RESET_CONTENT);
 	}
 
 	/**
@@ -606,15 +695,69 @@ public abstract class AbstractResource implements IResource
 		{
 			throw new IllegalArgumentException("you are not allowed to directly access header [" +
 				name + "], " + "use one of the other specialized methods of " +
-					Classes.simpleName(getClass()) + " to get or modify its value");
+						Classes.simpleName(getClass()) + " to get or modify its value");
 		}
 	}
 
 	/**
-	 * @param data
+	 * Reads the plain request header information and applies enriched information as meta data to
+	 * the current request. Those information are available for the whole request cycle.
+	 *
+	 * @param attributes
+	 *            the attributes to get the plain request header information
+	 */
+	protected void setRequestMetaData(Attributes attributes)
+	{
+		Request request = attributes.getRequest();
+		if (request instanceof WebRequest)
+		{
+			WebRequest webRequest = (WebRequest)request;
+
+			setRequestRangeMetaData(webRequest);
+		}
+	}
+
+	protected void setRequestRangeMetaData(WebRequest webRequest)
+	{
+		String rangeHeader = webRequest.getHeader("range");
+
+		// The content range header is only be calculated if a range is given
+		if (!Strings.isEmpty(rangeHeader) &&
+				rangeHeader.contains(ContentRangeType.BYTES.getTypeName()))
+		{
+			// fixing white spaces
+			rangeHeader = rangeHeader.replaceAll(" ", "");
+
+			String range = rangeHeader.substring(rangeHeader.indexOf('=') + 1,
+					rangeHeader.length());
+
+			String[] rangeParts = Strings.split(range, '-');
+
+			String startByteString = rangeParts[0];
+			String endByteString = rangeParts[1];
+
+			Long startbyte = startByteString != null && !startByteString.trim().equals("")
+					? Long.parseLong(startByteString) : 0;
+			Long endbyte = endByteString != null && !endByteString.trim().equals("")
+					? Long.parseLong(endByteString) : -1;
+
+			// Make the content range information available for the whole request cycle
+			RequestCycle.get().setMetaData(CONTENT_RANGE_STARTBYTE, startbyte);
+			RequestCycle.get().setMetaData(CONTENT_RANGE_ENDBYTE, endbyte);
+		}
+	}
+
+	/**
+	 * Sets the response header of resource response to the response received from the attributes
+	 *
+	 * @param resourceResponse
+	 *            the resource response to get the header fields from
 	 * @param attributes
+	 *            the attributes to get the response from to which the header information are going
+	 *            to be applied
 	 */
-	protected void setResponseHeaders(final ResourceResponse data, final Attributes attributes)
+	protected void setResponseHeaders(final ResourceResponse resourceResponse,
+		final Attributes attributes)
 	{
 		Response response = attributes.getResponse();
 		if (response instanceof WebResponse)
@@ -622,38 +765,36 @@ public abstract class AbstractResource implements IResource
 			WebResponse webResponse = (WebResponse)response;
 
 			// 1. Last Modified
-			Time lastModified = data.getLastModified();
+			Time lastModified = resourceResponse.getLastModified();
 			if (lastModified != null)
 			{
 				webResponse.setLastModifiedTime(lastModified);
 			}
 
 			// 2. Caching
-			configureCache(data, attributes);
+			configureCache(resourceResponse, attributes);
 
-			if (data.getErrorCode() != null)
+			if (resourceResponse.getErrorCode() != null)
 			{
-				webResponse.sendError(data.getErrorCode(), data.getErrorMessage());
+				webResponse.sendError(resourceResponse.getErrorCode(),
+					resourceResponse.getErrorMessage());
 				return;
 			}
 
-			if (data.getStatusCode() != null)
+			if (resourceResponse.getStatusCode() != null)
 			{
-				webResponse.setStatus(data.getStatusCode());
+				webResponse.setStatus(resourceResponse.getStatusCode());
 			}
-			
-			if (!data.dataNeedsToBeWritten(attributes))
+
+			if (!resourceResponse.dataNeedsToBeWritten(attributes))
 			{
 				webResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
 				return;
 			}
 
-			String fileName = data.getFileName();
-			ContentDisposition disposition = data.getContentDisposition();
-			String mimeType = data.getContentType();
-			long contentLength = data.getContentLength();
-
 			// 3. Content Disposition
+			String fileName = resourceResponse.getFileName();
+			ContentDisposition disposition = resourceResponse.getContentDisposition();
 			if (ContentDisposition.ATTACHMENT == disposition)
 			{
 				webResponse.setAttachmentHeader(fileName);
@@ -664,10 +805,11 @@ public abstract class AbstractResource implements IResource
 			}
 
 			// 4. Mime Type (+ encoding)
+			String mimeType = resourceResponse.getContentType();
 			if (mimeType != null)
 			{
-				final String encoding = data.getTextEncoding();
-				
+				final String encoding = resourceResponse.getTextEncoding();
+
 				if (encoding == null)
 				{
 					webResponse.setContentType(mimeType);
@@ -678,14 +820,46 @@ public abstract class AbstractResource implements IResource
 				}
 			}
 
-			// 5. Content Length
-			if (contentLength != -1)
+			// 5. Accept Range
+			ContentRangeType acceptRange = resourceResponse.getAcceptRange();
+			if (acceptRange != null)
+			{
+				webResponse.setAcceptRange(acceptRange.getTypeName());
+			}
+
+			long contentLength = resourceResponse.getContentLength();
+			boolean contentRangeApplied = false;
+
+			// 6. Content Range
+			// for more information take a look here:
+			// http://stackoverflow.com/questions/8293687/sample-http-range-request-session
+			// if the content range header has been set directly
+			// to the resource response use it otherwise calculate it
+			String contentRange = resourceResponse.getContentRange();
+			if (contentRange != null)
+			{
+				webResponse.setContentRange(contentRange);
+			}
+			else
+			{
+				// content length has to be set otherwise the content range header can not be
+				// calculated - accept range must be set to bytes - others are not supported at the
+				// moment
+				if (contentLength != -1 && ContentRangeType.BYTES.equals(acceptRange))
+				{
+					contentRangeApplied = setResponseContentRangeHeaderFields(webResponse,
+						attributes, contentLength);
+				}
+			}
+
+			// 7. Content Length
+			if (contentLength != -1 && !contentRangeApplied)
 			{
 				webResponse.setContentLength(contentLength);
 			}
 
 			// add custom headers and values
-			final HttpHeaderCollection headers = data.getHeaders();
+			final HttpHeaderCollection headers = resourceResponse.getHeaders();
 
 			for (String name : headers.getHeaderNames())
 			{
@@ -698,6 +872,50 @@ public abstract class AbstractResource implements IResource
 			}
 		}
 	}
+
+	/**
+	 * Sets the content range header fields to the given web response
+	 *
+	 * @param webResponse
+	 *            the web response to apply the content range information to
+	 * @param attributes
+	 *            the attributes to get the request from
+	 * @param contentLength
+	 *            the content length of the response
+	 * @return if the content range header information has been applied
+	 */
+	protected boolean setResponseContentRangeHeaderFields(WebResponse webResponse,
+		Attributes attributes, long contentLength)
+	{
+		boolean contentRangeApplied = false;
+		if (attributes.getRequest() instanceof WebRequest)
+		{
+			Long startbyte = RequestCycle.get().getMetaData(CONTENT_RANGE_STARTBYTE);
+			Long endbyte = RequestCycle.get().getMetaData(CONTENT_RANGE_ENDBYTE);
+
+			if (startbyte != null && endbyte != null)
+			{
+				// if end byte hasn't been set
+				if (endbyte == -1)
+				{
+					endbyte = contentLength - 1;
+				}
+
+				// Change the status code to 206 partial content
+				webResponse.setStatus(206);
+				// currently only bytes are supported.
+				webResponse.setContentRange(ContentRangeType.BYTES.getTypeName() + " " + startbyte +
+					'-' + endbyte + '/' + contentLength);
+				// content length must be overridden by the recalculated one
+				webResponse.setContentLength((endbyte - startbyte) + 1);
+
+				// content range has been applied do not set the content length again!
+				contentRangeApplied = true;
+			}
+		}
+		return contentRangeApplied;
+	}
+
 	/**
 	 * Callback invoked when resource data needs to be written to response. Subclass needs to
 	 * implement the {@link #writeData(org.apache.wicket.request.resource.IResource.Attributes)}

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
index 8c5b457..ab66014 100644
--- a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
@@ -136,15 +136,13 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 	 * controls whether {@link org.apache.wicket.request.resource.caching.IResourceCachingStrategy}
 	 * should be applied to resource
 	 */
-	
 	private boolean cachingEnabled = true;
-	
+
 	/**
 	 * text encoding (may be null) - only makes sense for character-based resources
 	 */
-	
 	private String textEncoding = null;
-	
+
 	/**
 	 * Hidden constructor.
 	 * 
@@ -189,7 +187,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		{
 			return Session.get().getLocale();
 		}
-		
+
 		return locale;
 	}
 
@@ -199,7 +197,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		{
 			return Session.get().getStyle();
 		}
-		
+
 		return style;
 	}
 
@@ -216,7 +214,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 
 	/**
 	 * get text encoding (intented for character-based resources)
-	 
+	 *
 	 * @return custom encoding or {@code null} to use default
 	 */
 	public String getTextEncoding()
@@ -288,7 +286,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		if (resourceStream == null)
 		{
 			return sendResourceError(resourceResponse, HttpServletResponse.SC_NOT_FOUND,
-					"Unable to find resource");
+						"Unable to find resource");
 		}
 
 		// add Last-Modified header (to support HEAD requests and If-Modified-Since)
@@ -307,27 +305,28 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 
 			// set Content-Type (may be null)
 			resourceResponse.setContentType(contentType);
-			
+
 			// set content encoding (may be null)
 			resourceResponse.setTextEncoding(getTextEncoding());
 
+			// supports accept range
+			resourceResponse.setAcceptRange(ContentRangeType.BYTES);
+
 			try
 			{
-				// read resource data
-				final byte[] bytes = IOUtils.toByteArray(resourceStream.getInputStream());
+				// read resource data to get the content length
+				long contentLength = IOUtils.toByteArray(resourceStream.getInputStream()).length;
 
 				// send Content-Length header
-				resourceResponse.setContentLength(bytes.length);
+				resourceResponse.setContentLength(contentLength);
+
+				// get content range information
+				Long startbyte = RequestCycle.get().getMetaData(CONTENT_RANGE_STARTBYTE);
+				Long endbyte = RequestCycle.get().getMetaData(CONTENT_RANGE_ENDBYTE);
 
 				// send response body with resource data
-				resourceResponse.setWriteCallback(new WriteCallback()
-				{
-					@Override
-					public void writeData(Attributes attributes)
-					{
-						attributes.getResponse().write(bytes);
-					}
-				});
+				resourceResponse.setWriteCallback(new PartWriterCallback(
+					resourceStream.getInputStream(), contentLength, startbyte, endbyte));
 			}
 			catch (IOException e)
 			{
@@ -408,7 +407,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 	{
 		return internalGetResourceStream(getCurrentStyle(), getCurrentLocale());
 	}
-	
+
 	/**
 	 * locate resource stream for current resource
 	 * 
@@ -421,7 +420,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 
 	/**
 	 * @return whether {@link org.apache.wicket.resource.ITextResourceCompressor} can be used to compress the
-	 *         resource.
+	 *          resource.
 	 */
 	public boolean getCompress()
 	{
@@ -466,8 +465,8 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		if (accept(realPath) == false)
 		{
 			throw new PackageResourceBlockedException(
-					"Access denied to (static) package resource " + absolutePath +
-						". See IPackageResourceGuard");
+							"Access denied to (static) package resource " + absolutePath +
+									". See IPackageResourceGuard");
 		}
 
 		if (resourceStream != null)
@@ -478,11 +477,12 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 	}
 
 	/**
-	 * An IResourceStream that processes the input stream of the original
-	 * IResourceStream
+	 * An IResourceStream that processes the input stream of the original IResourceStream
 	 */
 	private class ProcessingResourceStream extends ResourceStreamWrapper
 	{
+		private static final long serialVersionUID = 1L;
+
 		private ProcessingResourceStream(IResourceStream delegate)
 		{
 			super(delegate);
@@ -496,10 +496,12 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 			try
 			{
 				bytes = IOUtils.toByteArray(inputStream);
-			} catch (IOException iox)
+			}
+			catch (IOException iox)
 			{
 				throw new WicketRuntimeException(iox);
-			} finally
+			}
+			finally
 			{
 				IOUtils.closeQuietly(this);
 			}
@@ -523,8 +525,8 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 	/**
 	 * Checks whether access is granted for this resource.
 	 *
-	 * By default IPackageResourceGuard is used to check the permissions but
-	 * the resource itself can also make the check.
+	 * By default IPackageResourceGuard is used to check the permissions but the resource itself can
+	 * also make the check.
 	 *
 	 * @param path
 	 *            resource path
@@ -548,7 +550,8 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 	 */
 	public static boolean exists(final ResourceReference.Key key)
 	{
-		return exists(key.getScopeClass(), key.getName(), key.getLocale(), key.getStyle(), key.getVariation());
+		return exists(key.getScopeClass(), key.getName(), key.getLocale(), key.getStyle(),
+			key.getVariation());
 	}
 
 	/**
@@ -622,15 +625,13 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 			return false;
 		if (getClass() != obj.getClass())
 			return false;
-		
+
 		PackageResource other = (PackageResource)obj;
-		
-		return Objects.equals(absolutePath, other.absolutePath)
-			&& Objects.equals(locale, other.locale)
-			&& Objects.equals(path, other.path)
-			&& Objects.equals(scopeName, other.scopeName)
-			&& Objects.equals(style, other.style)
-			&& Objects.equals(variation, other.variation);
+
+		return Objects.equals(absolutePath, other.absolutePath) &&
+			Objects.equals(locale, other.locale) && Objects.equals(path, other.path) &&
+			Objects.equals(scopeName, other.scopeName) && Objects.equals(style, other.style) &&
+			Objects.equals(variation, other.variation);
 	}
 
 	String getParentFolderPlaceholder()
@@ -675,12 +676,11 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 				return false;
 
 			CacheKey cacheKey = (CacheKey)o;
-			
-			return Objects.equals(locale, cacheKey.locale)
-				&& Objects.equals(path, cacheKey.path)
-				&& Objects.equals(scopeName, cacheKey.scopeName)
-				&& Objects.equals(style, cacheKey.style)
-				&& Objects.equals(variation, cacheKey.variation);
+
+			return Objects.equals(locale, cacheKey.locale) && Objects.equals(path, cacheKey.path) &&
+				Objects.equals(scopeName, cacheKey.scopeName) &&
+				Objects.equals(style, cacheKey.style) &&
+				Objects.equals(variation, cacheKey.variation);
 		}
 
 		@Override

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java
new file mode 100644
index 0000000..ca70470
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.request.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.wicket.protocol.http.servlet.ResponseIOException;
+import org.apache.wicket.request.resource.AbstractResource.WriteCallback;
+import org.apache.wicket.request.resource.IResource.Attributes;
+import org.apache.wicket.util.io.Streams;
+
+/**
+ * Used to read a part of an input stream and writes it to the output stream of the response taken
+ * from attributes of the writeData method.
+ *
+ * @author Tobias Soloschenko
+ *
+ */
+public class PartWriterCallback extends WriteCallback
+{
+	private final InputStream inputStream;
+
+	private final Long contentLength;
+
+	private final Long startbyte;
+
+	private Long endbyte;
+
+	private int bufferSize;
+
+
+	/**
+	 * Creates a part writer callback.<br>
+	 * <br>
+	 * Reads a part of the given input stream. If the startbyte parameter is not null the number of
+	 * bytes are skipped till the stream is read. If the endbyte is not null the stream is read till
+	 * endbyte, else to the end of the whole stream. If startbyte and endbyte is null the whole
+	 * stream is read.
+	 *
+	 * @param inputStream
+	 *            the input stream to be read
+	 * @param the
+	 *            content length
+	 * @param startbyte
+	 *            the start position to read from (if not null the number of bytes are skipped till
+	 *            the stream is read)
+	 * @param endbyte
+	 *            the end position to read to (if not null the stream is going to be read till
+	 *            endbyte, else to the end of the whole stream)
+	 */
+	public PartWriterCallback(InputStream inputStream, Long contentLength, Long startbyte,
+		Long endbyte)
+	{
+		this.inputStream = inputStream;
+		this.contentLength = contentLength;
+		this.startbyte = startbyte;
+		this.endbyte = endbyte;
+	}
+
+	/**
+	 * Writes the data
+	 *
+	 * @param attributes
+	 *            the attributes to get the output stream of the response
+	 * @throws IOException
+	 *             if something went wrong while writing the data to the output stream
+	 */
+	@Override
+	public void writeData(Attributes attributes) throws IOException
+	{
+		try
+		{
+			OutputStream outputStream = attributes.getResponse().getOutputStream();
+			byte[] buffer = new byte[getBufferSize()];
+
+			if (startbyte != null || endbyte != null)
+			{
+				// skipping the first bytes which are
+				// requested to be skipped by the client
+				if (startbyte != null)
+				{
+					inputStream.skip(startbyte);
+				}
+
+				// If there are no end bytes given read the whole stream till the end
+				if (endbyte == null || Long.valueOf(-1).equals(endbyte))
+				{
+					endbyte = contentLength;
+				}
+
+				long totalBytes = 0;
+				int actualReadBytes;
+
+				while ((actualReadBytes = inputStream.read(buffer)) != -1)
+				{
+					totalBytes = totalBytes + buffer.length;
+					long lowerBuffer = endbyte - totalBytes;
+					if (lowerBuffer <= 0)
+					{
+						buffer = resizeArray(buffer, actualReadBytes);
+						outputStream.write(buffer);
+						break;
+					}
+					else
+					{
+						outputStream.write(buffer);
+					}
+				}
+			}
+			else
+			{
+				Streams.copy(inputStream, outputStream, getBufferSize());
+			}
+		}
+		catch (ResponseIOException e)
+		{
+			// the client has closed the connection and
+			// doesn't read the stream further on
+			// (in tomcats
+			// org.apache.catalina.connector.ClientAbortException)
+			// we ignore this case
+		}
+	}
+
+	/**
+	 * Reallocates an array with a new size, and copies the contents of the old array to the new
+	 * array.
+	 *
+	 * @param oldArray
+	 *            the old array, to be reallocated.
+	 * @param newSize
+	 *            the new array size.
+	 * @return A new array with the same contents.
+	 */
+	private static byte[] resizeArray(byte[] oldArray, int newSize)
+	{
+		int oldSize = oldArray.length;
+		byte[] newArray = new byte[newSize];
+		int minLength = Math.min(oldSize, newSize);
+		if (minLength > 0)
+		{
+			System.arraycopy(oldArray, 0, newArray, 0, minLength);
+		}
+		return newArray;
+	}
+
+	/**
+	 * Sets the buffer size used to send the data to the client
+	 *
+	 * @return the buffer size used to send the data to the client (default is 4096)
+	 */
+	public int getBufferSize()
+	{
+		return bufferSize > 0 ? bufferSize : 4096;
+	}
+
+	/**
+	 * Sets the buffer size used to send the data to the client
+	 *
+	 * @param bufferSize
+	 *            the buffer size used to send the data to the client
+	 */
+	public void setBufferSize(int bufferSize)
+	{
+		this.bufferSize = bufferSize;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java
index b191c35..d4ad5b3 100644
--- a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java
@@ -29,18 +29,19 @@ public class MediaTagsExtendedTestPage extends WebPage
 
 	public MediaTagsExtendedTestPage()
 	{
-		Video video = new Video("video", new MediaStreamingResourceReference(
-			MediaTagsTestPage.class, "dummyVideo.m4a"));
+		Video video = new Video("video", new PackageResourceReference(MediaTagsTestPage.class,
+			"dummyVideo.m4a"));
 
 		// source tag
-		Source source = new Source("source","http://www.mytestpage.xc/video.m4a");
+		Source source = new Source("source", "http://www.mytestpage.xc/video.m4a");
 		source.setMedia("screen and (device-width:500px)");
 		source.setType("video/mp4");
 		source.setDisplayType(true);
 		video.add(source);
 
 		// tack tag
-		Track track = new Track("track", new PackageResourceReference(MediaTagsTestPage.class,"dummySubtitles.vtt"));
+		Track track = new Track("track", new PackageResourceReference(MediaTagsTestPage.class,
+			"dummySubtitles.vtt"));
 		track.setKind(Kind.SUBTITLES);
 		track.setLabel("Subtitles of video");
 		track.setSrclang(Locale.GERMANY);

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java
index e8dbdd8..edc3ab4 100644
--- a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java
@@ -31,8 +31,8 @@ public class MediaTagsTestPage extends WebPage
 	{
 		PageParameters pageParameters = new PageParameters();
 		pageParameters.set("test", "test");
-		Audio audio = new Audio("audio", new MediaStreamingResourceReference(
-			MediaTagsTestPage.class, "dummyAudio.mp3"), pageParameters);
+		Audio audio = new Audio("audio", new PackageResourceReference(MediaTagsTestPage.class,
+			"dummyAudio.mp3"), pageParameters);
 		audio.setAutoplay(true);
 		audio.setControls(true);
 		audio.setCrossOrigin(Cors.USER_CREDENTIALS);
@@ -42,8 +42,8 @@ public class MediaTagsTestPage extends WebPage
 		audio.setEndTime("10");
 		add(audio);
 
-		Video video = new Video("video", new MediaStreamingResourceReference(
-			MediaTagsTestPage.class, "dummyVideo.m4a"));
+		Video video = new Video("video", new PackageResourceReference(MediaTagsTestPage.class,
+			"dummyVideo.m4a"));
 		PageParameters pageParameters2 = new PageParameters();
 		pageParameters2.add("test2", "test2");
 		video.setPoster(new PackageResourceReference(MediaTagsTestPage.class, "dummyPoster.jpg"),

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java b/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java
index 87d70f5..9055ff1 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java
@@ -21,7 +21,6 @@ import java.util.UUID;
 import org.apache.wicket.examples.WicketExamplePage;
 import org.apache.wicket.markup.head.CssHeaderItem;
 import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.html.media.MediaStreamingResourceReference;
 import org.apache.wicket.markup.html.media.Source;
 import org.apache.wicket.markup.html.media.video.Video;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
@@ -50,7 +49,7 @@ public final class Home extends WicketExamplePage
 	{
 		// Internal video with several options
 
-		Video video1 = new Video("video1", new MediaStreamingResourceReference(Home.class,
+		Video video1 = new Video("video1", new PackageResourceReference(Home.class,
 			"video.mp4"));
 		video1.setAutoplay(false);
 		video1.setControls(true);
@@ -65,7 +64,7 @@ public final class Home extends WicketExamplePage
 		Video video2 = new Video("video2");
 		video2.setPoster(new PackageResourceReference(Home.class, "novideo.gif"));
 
-		Source source2 = new Source("source2", new MediaStreamingResourceReference(Home.class,
+		Source source2 = new Source("source2", new PackageResourceReference(Home.class,
 			"video.mp4"));
 		// Need to be set to true to show the type
 		source2.setDisplayType(true);
@@ -86,7 +85,7 @@ public final class Home extends WicketExamplePage
 
 		/*
 		 * // video with track
-		 * Video video4 = new Video("video4", new MediaStreamingResourceReference(Home.class, "dummyVideo.m4a"));
+		 * Video video4 = new Video("video4", new PackageResourceReference(Home.class, "dummyVideo.m4a"));
 		 *
 		 * // source tag
 		 * Source source4 = new Source("source4", "http://www.mytestpage.xc/video.m4a");

http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java
----------------------------------------------------------------------
diff --git a/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java b/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java
index 0566d0c..a790d0b 100644
--- a/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java
+++ b/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java
@@ -97,6 +97,32 @@ public abstract class WebResponse extends Response
 	public abstract void setContentType(final String mimeType);
 
 	/**
+	 * Sets the content range of the response. If no content range is set the client assumes the
+	 * whole content. Please note that if the content range is set, the content length, the status
+	 * code and the accept range must be set right, too.
+	 *
+	 * @param contentRange
+	 *            the content range
+	 */
+	public void setContentRange(final String contentRange)
+	{
+		setHeader("Content-Range", contentRange);
+	}
+
+
+	/**
+	 * Sets the accept range (e.g. bytes)
+	 *
+	 * @param acceptRange
+	 *            the accept range header information
+	 */
+	public void setAcceptRange(final String acceptRange)
+	{
+		setHeader("Accept-Range", acceptRange);
+
+	}
+
+	/**
 	 * Set the contents last modified time, if appropriate in the subclass.
 	 * 
 	 * @param time
@@ -138,19 +164,18 @@ public abstract class WebResponse extends Response
 	}
 
 	/**
-	 * <a href="http://greenbytes.de/tech/tc2231/">Encodes</a> the value of the filename
-	 * used in "Content-Disposition" response header
+	 * <a href="http://greenbytes.de/tech/tc2231/">Encodes</a> the value of the filename used in
+	 * "Content-Disposition" response header
 	 *
 	 * @param filename
-	 *          the non-encoded file name
+	 *            the non-encoded file name
 	 * @return encoded filename
 	 */
 	private String encodeDispositionHeaderValue(final String filename)
 	{
-		return 	(Strings.isEmpty(filename) ?
-						"" :
-						String.format("; filename=\"%1$s\"; filename*=UTF-8''%1$s",
-								UrlEncoder.PATH_INSTANCE.encode(filename, "UTF-8")));
+		return (Strings.isEmpty(filename) ? "" : String.format(
+			"; filename=\"%1$s\"; filename*=UTF-8''%1$s",
+			UrlEncoder.PATH_INSTANCE.encode(filename, "UTF-8")));
 	}
 
 	/**
@@ -213,9 +238,9 @@ public abstract class WebResponse extends Response
 
 	/**
 	 * Make this response cacheable
-	 * <p/> 
-	 * when trying to enable caching for web pages check this out: 
-	 * <a href="https://issues.apache.org/jira/browse/WICKET-4357">WICKET-4357</a>
+	 * <p/>
+	 * when trying to enable caching for web pages check this out: <a
+	 * href="https://issues.apache.org/jira/browse/WICKET-4357">WICKET-4357</a>
 	 * 
 	 * @param duration
 	 *            maximum duration before the response must be invalidated by any caches. It should
@@ -223,7 +248,7 @@ public abstract class WebResponse extends Response
 	 *            href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC-2616</a>.
 	 * @param scope
 	 *            controls which caches are allowed to cache the response
-	 *            
+	 *
 	 * @see WebResponse#MAX_CACHE_DURATION
 	 */
 	public void enableCaching(Duration duration, final WebResponse.CacheScope scope)
@@ -248,7 +273,7 @@ public abstract class WebResponse extends Response
 
 		// Set cache scope
 		setHeader("Cache-Control", scope.cacheControl);
-		
+
 		// Set maximum age for caching in seconds (rounded)
 		addHeader("Cache-Control", "max-age=" + Math.round(duration.seconds()));
 
@@ -266,7 +291,8 @@ public abstract class WebResponse extends Response
 	 * href="http://palisade.plynt.com/issues/2008Jul/cache-control-attributes">here</a> or in <a
 	 * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC-2616</a>.
 	 */
-	public static enum CacheScope {
+	public static enum CacheScope
+	{
 		/**
 		 * use all caches (private + public)
 		 * <p/>