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:27 UTC

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

Repository: wicket
Updated Branches:
  refs/heads/master 792a0a413 -> a1d0df298


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

Add unit tests for the new ranges support. Fix some corner cases


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

Branch: refs/heads/master
Commit: 4a36aa74718739c44db925af4ff34b6f37cb75fd
Parents: f623cea
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Thu Mar 26 21:39:56 2015 +0200
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Mon Mar 30 21:59:14 2015 +0300

----------------------------------------------------------------------
 .../request/resource/PartWriterCallback.java    | 47 ++++++---------
 .../resource/PackageResourceReferenceTest.java  | 63 ++++++++++++++++++++
 2 files changed, 80 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/4a36aa74/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
index ca70470..c0c7a63 100644
--- 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
@@ -104,22 +104,31 @@ public class PartWriterCallback extends WriteCallback
 					endbyte = contentLength;
 				}
 
+				// The read bytes in the current buffer
+				int readBytes;
+
+				// The total bytes read
 				long totalBytes = 0;
-				int actualReadBytes;
 
-				while ((actualReadBytes = inputStream.read(buffer)) != -1)
+				while ((readBytes = inputStream.read(buffer)) != -1)
 				{
-					totalBytes = totalBytes + buffer.length;
-					long lowerBuffer = endbyte - totalBytes;
-					if (lowerBuffer <= 0)
+					totalBytes += readBytes;
+
+					// Check if the end byte is reached
+					if (endbyte - totalBytes < 0)
 					{
-						buffer = resizeArray(buffer, actualReadBytes);
-						outputStream.write(buffer);
+						// calculate the bytes left to be read in the current buffer
+						// can be casted to int, because the the previous chunks are
+						// subtracted - so it can't exceed buffer size
+						int leftBytesToBeRead = (int)(totalBytes - startbyte) -
+							(int)(totalBytes - endbyte);
+						outputStream.write(buffer, 0, leftBytesToBeRead);
 						break;
 					}
 					else
 					{
-						outputStream.write(buffer);
+						// If the end byte is not reached read the full buffer
+						outputStream.write(buffer, 0, readBytes);
 					}
 				}
 			}
@@ -139,28 +148,6 @@ public class PartWriterCallback extends WriteCallback
 	}
 
 	/**
-	 * 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)

http://git-wip-us.apache.org/repos/asf/wicket/blob/4a36aa74/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java b/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
index 4fcbd09..a58a2c9 100644
--- a/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
@@ -20,6 +20,11 @@ import java.util.Locale;
 
 import org.apache.wicket.Application;
 import org.apache.wicket.WicketTestCase;
+import org.apache.wicket.protocol.http.mock.MockHttpServletRequest;
+import org.apache.wicket.protocol.http.mock.MockHttpServletResponse;
+import org.apache.wicket.request.Request;
+import org.apache.wicket.request.Response;
+import org.apache.wicket.request.resource.AbstractResource.ContentRangeType;
 import org.apache.wicket.request.resource.IResource.Attributes;
 import org.apache.wicket.request.resource.ResourceReference.UrlAttributes;
 import org.apache.wicket.response.ByteArrayResponse;
@@ -237,4 +242,62 @@ public class PackageResourceReferenceTest extends WicketTestCase
 		final CssPackageResource alreadyMinifiedResource = alreadyMinified.getResource();
 		Assert.assertFalse("Already minified resource should got its compress flag set to false", alreadyMinifiedResource.getCompress());
 	}
+
+	/**
+	 * See WICKET-5819 - Media tags
+	 */
+	@Test
+	public void testContentRange()
+	{
+		// Test range
+		Assert.assertEquals("resource", makeRangeRequest("bytes=0-8"));
+		Assert.assertEquals("ource", makeRangeRequest("bytes=3-8"));
+		Assert.assertEquals("resource_var_style_en.txt", makeRangeRequest("bytes=0-"));
+		Assert.assertEquals("var_style_en.txt", makeRangeRequest("bytes=9-"));
+		Assert.assertEquals("resource_var_style_en.txt", makeRangeRequest("bytes=-"));
+		Assert.assertEquals("resource_var_style_en.txt", makeRangeRequest("bytes=-25"));
+	}
+
+	private String makeRangeRequest(String range)
+	{
+		ResourceReference reference = new PackageResourceReference(scope, "resource.txt",
+			locales[1], styles[1], variations[1]);
+
+		ByteArrayResponse byteResponse = new ByteArrayResponse();
+
+		Request request = tester.getRequestCycle().getRequest();
+		MockHttpServletRequest mockHttpServletRequest = (MockHttpServletRequest)request.getContainerRequest();
+		mockHttpServletRequest.setHeader("range", range);
+		Attributes mockAttributes = new Attributes(request, byteResponse);
+		reference.getResource().respond(mockAttributes);
+		return new String(byteResponse.getBytes());
+	}
+
+	/**
+	 * See WICKET-5819 - Media tags
+	 */
+	@Test
+	public void testContentRangeHeaders()
+	{
+		// Test header fields
+		ResourceReference reference = new PackageResourceReference(scope, "resource.txt",
+			locales[1], styles[1], variations[1]);
+		Request request = tester.getRequestCycle().getRequest();
+		Response response = tester.getRequestCycle().getResponse();
+		MockHttpServletResponse mockHttpServletResponse = (MockHttpServletResponse)response.getContainerResponse();
+		Attributes mockAttributes = new Attributes(request, response);
+		reference.getResource().respond(mockAttributes);
+		Assert.assertEquals(ContentRangeType.BYTES.getTypeName(),
+			mockHttpServletResponse.getHeader("Accept-Range"));
+		// For normal: If a resource supports content range no content is delivered
+		// if no "Range" header is given, but we have to deliver it, because
+		// other resources then media should get the content. (e.g. CSS, JS, etc.) Browsers
+		// detecting media requests and automatically add the "Range" header for
+		// partial content and they don't make an initial request to detect if a media
+		// resource supports Content-Range (by the Accept-Range header)
+		Assert.assertEquals("resource_var_style_en.txt",
+			new String(mockHttpServletResponse.getBinaryContent()));
+	}
+
+
 }


[3/4] wicket git commit: Revert "Revert merging for WICKET-5819"

Posted by mg...@apache.org.
Revert "Revert merging for WICKET-5819"

This reverts commit 37116aff50ba0d4382c4b3371eb537bad29fa48d.


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

Branch: refs/heads/master
Commit: 499f11fdaf8783b3c795a93e9af7870aa2f84f3c
Parents: 792a0a4
Author: Andrea Del Bene <ad...@apache.org>
Authored: Fri Mar 13 21:40:20 2015 +0100
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Mon Mar 30 21:59:14 2015 +0300

----------------------------------------------------------------------
 .../util/resource/PackageResourceStream.java    |  27 +-
 .../markup/html/media/MediaComponent.java       | 500 +++++++++++++++++++
 .../media/MediaStreamingResourceReference.java  | 182 +++++++
 .../markup/html/media/PartWriterCallback.java   | 185 +++++++
 .../apache/wicket/markup/html/media/Source.java | 240 +++++++++
 .../apache/wicket/markup/html/media/Track.java  | 274 ++++++++++
 .../wicket/markup/html/media/audio/Audio.java   |  95 ++++
 .../wicket/markup/html/media/video/Video.java   | 210 ++++++++
 .../html/media/MediaComponentsApplication.java  |  52 ++
 .../html/media/MediaTagsExtendedTestPage.html   |  12 +
 .../html/media/MediaTagsExtendedTestPage.java   |  53 ++
 .../wicket/markup/html/media/MediaTagsTest.java |  92 ++++
 .../markup/html/media/MediaTagsTestPage.html    |  10 +
 .../markup/html/media/MediaTagsTestPage.java    |  56 +++
 .../wicket/markup/html/media/dummyAudio.mp3     |   0
 .../wicket/markup/html/media/dummyPoster.jpg    |   0
 .../wicket/markup/html/media/dummySubtitles.vtt |   0
 .../wicket/markup/html/media/dummyVideo.m4a     |   0
 wicket-examples/pom.xml                         |   2 +
 .../org/apache/wicket/examples/media/Home.css   |  32 ++
 .../org/apache/wicket/examples/media/Home.html  |  29 ++
 .../org/apache/wicket/examples/media/Home.java  | 116 +++++
 .../examples/media/VideosApplication.java       |  63 +++
 .../apache/wicket/examples/media/novideo.gif    | Bin 0 -> 25903 bytes
 .../org/apache/wicket/examples/media/video.mp4  | Bin 0 -> 2757913 bytes
 .../wicket/examples/homepage/HomePage.html      |   1 +
 wicket-examples/src/main/webapp/WEB-INF/web.xml |  22 +-
 27 files changed, 2248 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/main/java/org/apache/wicket/core/util/resource/PackageResourceStream.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/resource/PackageResourceStream.java b/wicket-core/src/main/java/org/apache/wicket/core/util/resource/PackageResourceStream.java
index e76926d..4a1a4e6 100644
--- a/wicket-core/src/main/java/org/apache/wicket/core/util/resource/PackageResourceStream.java
+++ b/wicket-core/src/main/java/org/apache/wicket/core/util/resource/PackageResourceStream.java
@@ -18,6 +18,7 @@ package org.apache.wicket.core.util.resource;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Locale;
 
 import org.apache.wicket.Application;
 import org.apache.wicket.WicketRuntimeException;
@@ -36,10 +37,10 @@ import org.apache.wicket.util.time.Time;
  * {@link IResourceStreamLocator}.
  *
  * @author <a href="mailto:jbq@apache.org">Jean-Baptiste Quenot</a>
+ * @author Tobias Soloschenko
  */
 public class PackageResourceStream extends AbstractResourceStream
 {
-	/** */
 	private static final long serialVersionUID = 1L;
 
 	private final IResourceStream resourceStream;
@@ -56,11 +57,33 @@ public class PackageResourceStream extends AbstractResourceStream
 	 */
 	public PackageResourceStream(Class<?> scope, String path)
 	{
+		this(scope, path, null, null, null);
+	}
+
+	/**
+	 * Obtains an {@link IResourceStream} from the application's
+	 * {@link IResourceStreamLocator#locate(Class, String)}
+	 *
+	 * @param scope
+	 *            This argument will be used to get the class loader for loading the package
+	 *            resource, and to determine what package it is in.
+	 * @param path
+	 *            The path to the resource
+	 * @param locale
+	 *            the locale of the resource to get
+	 * @param style
+	 *            the style of the resource to get
+	 * @param variation
+	 *            the variation of the resource to get
+	 */
+	public PackageResourceStream(Class<?> scope, String path, Locale locale, String style,
+		String variation)
+	{
 		String absolutePath = Packages.absolutePath(scope, path);
 		resourceStream = Application.get()
 			.getResourceSettings()
 			.getResourceStreamLocator()
-			.locate(scope, absolutePath, null, null, null, null, false);
+			.locate(scope, absolutePath, style, variation, locale, null, false);
 
 		if (resourceStream == null)
 		{

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/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
new file mode 100755
index 0000000..093cd8e
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java
@@ -0,0 +1,500 @@
+/*
+ * 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 org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.IModel;
+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;
+
+/**
+ * The media component is used to provide basic functionality to the video and audio component. The
+ * given media streaming resource reference supports Content-Ranges and other stuff to make the
+ * audio and video playback smooth.
+ *
+ * @author Tobias Soloschenko
+ * @author Andrew Lombardi
+ */
+public abstract class MediaComponent extends WebMarkupContainer
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * To be used for the <em>crossorigin</em> attribute
+	 *
+	 * @see {@link #setCrossOrigin(Cors)}
+	 */
+	public enum Cors
+	{
+		ANONYMOUS("anonymous"), USER_CREDENTIALS("user-credentials"), NO_CORS("");
+
+		private final String realName;
+
+		private Cors(String realName)
+		{
+			this.realName = realName;
+		}
+
+		public String getRealName()
+		{
+			return realName;
+		}
+	}
+
+	/**
+	 * To be used for the <em>preload</em> attribute
+	 *
+	 * @see {@link #setPreload(Preload)}
+	 */
+	public enum Preload
+	{
+		NONE("none"), METADATA("metadata"), AUTO("auto");
+
+		public final String realName;
+
+		private Preload(String realname)
+		{
+			realName = realname;
+		}
+
+		public String getRealName()
+		{
+			return realName;
+		}
+	}
+
+	private boolean autoplay;
+
+	private boolean loop;
+
+	private boolean muted;
+
+	private boolean controls = true;
+
+	private Preload preload;
+
+	private String startTime;
+
+	private String endTime;
+
+	private String mediaGroup;
+
+	private Cors crossOrigin;
+
+	private final PageParameters pageParameters;
+
+	private final MediaStreamingResourceReference mediaStreamingResourceReference;
+
+	private final String url;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param id
+	 *            The component id
+	 */
+	public MediaComponent(String id)
+	{
+		this(id, null, null, null, null);
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param id
+	 *            The component id
+	 * @param model
+	 *            The component model
+	 */
+	public MediaComponent(String id, IModel<?> model)
+	{
+		this(id, model, null, null, null);
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param id
+	 *            The component id
+	 * @param mediaStreamingResourceReference
+	 */
+	public MediaComponent(String id, MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		this(id, null, null, null, mediaStreamingResourceReference);
+	}
+
+	public MediaComponent(String id, IModel<?> model,
+		MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		this(id, model, null, null, mediaStreamingResourceReference);
+	}
+
+	public MediaComponent(String id,
+		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PageParameters pageParameters)
+	{
+		this(id, null, null, pageParameters, mediaStreamingResourceReference);
+	}
+
+	public MediaComponent(String id, IModel<?> model,
+		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PageParameters pageParameters)
+	{
+		this(id, model, null, pageParameters, mediaStreamingResourceReference);
+	}
+
+	public MediaComponent(String id, String url)
+	{
+		this(id, null, url, null, null);
+	}
+
+	public MediaComponent(String id, IModel<?> model, String url)
+	{
+		this(id, model, url, null, null);
+	}
+
+	public MediaComponent(String id, IModel<?> model, String url, PageParameters pageParameters)
+	{
+		this(id, model, url, pageParameters, null);
+	}
+
+	private MediaComponent(String id, IModel<?> model, String url, PageParameters pageParameters,
+		MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		super(id, model);
+		this.url = url;
+		this.pageParameters = pageParameters;
+		this.mediaStreamingResourceReference = mediaStreamingResourceReference;
+	}
+
+	@Override
+	protected void onComponentTag(ComponentTag tag)
+	{
+		super.onComponentTag(tag);
+
+		// The time management is used to set the start / stop
+		// time in seconds of the movie to be played back
+		String timeManagement = "";
+		if (startTime != null)
+		{
+			timeManagement += "#t=" + startTime + (endTime != null ? "," + endTime : "");
+		}
+
+		if (mediaStreamingResourceReference != null)
+		{
+			CharSequence urlToMediaReference = RequestCycle.get().urlFor(
+				mediaStreamingResourceReference, pageParameters);
+			tag.put("src", urlToMediaReference + timeManagement);
+		}
+		else if (url != null)
+		{
+			Url encoded = new PageParametersEncoder().encodePageParameters(pageParameters);
+			String queryString = encoded.getQueryString();
+			tag.put("src", url + (queryString != null ? "?" + queryString : "") + timeManagement);
+		}
+
+		String mg = getMediaGroup();
+		if (mg != null)
+		{
+			tag.put("mediagroup", mg);
+		}
+
+		if (isAutoplay())
+		{
+			tag.put("autoplay", "autoplay");
+		}
+
+		if (isLooping())
+		{
+			tag.put("loop", "loop");
+		}
+
+		if (isMuted())
+		{
+			tag.put("muted", "muted");
+		}
+
+		if (hasControls())
+		{
+			tag.put("controls", "controls");
+		}
+
+		Preload _preload = getPreload();
+		if (_preload != null)
+		{
+			tag.put("preload", _preload.getRealName());
+		}
+
+		Cors cors = getCrossOrigin();
+		if (cors != null)
+		{
+			tag.put("crossorigin", cors.getRealName());
+		}
+	}
+
+	/**
+	 * If the playback is autoplayed on load
+	 *
+	 * @return If the playback is autoplayed on load
+	 */
+	public boolean isAutoplay()
+	{
+		return autoplay;
+	}
+
+	/**
+	 * Sets the playback to be autoplayed on load
+	 *
+	 * @param autoplay
+	 *            If the playback is autoplayed on load
+	 */
+	public void setAutoplay(boolean autoplay)
+	{
+		this.autoplay = autoplay;
+	}
+
+	/**
+	 * If the playback is looped
+	 *
+	 * @return If the playback is looped
+	 */
+	public boolean isLooping()
+	{
+		return loop;
+	}
+
+	/**
+	 * Sets the playback to be looped
+	 *
+	 * @param loop
+	 *            If the playback is looped
+	 */
+	public void setLooping(boolean loop)
+	{
+		this.loop = loop;
+	}
+
+	/**
+	 * If the playback is muted initially
+	 *
+	 * @return If the playback is muted initially
+	 */
+	public boolean isMuted()
+	{
+		return muted;
+	}
+
+	/**
+	 * Sets the playback muted initially
+	 *
+	 * @param muted
+	 *            If the playback is muted initially
+	 */
+	public void setMuted(boolean muted)
+	{
+		this.muted = muted;
+	}
+
+	/**
+	 * If the controls are going to be displayed
+	 *
+	 * @return if the controls are going to displayed
+	 */
+	public boolean hasControls()
+	{
+		return controls;
+	}
+
+	/**
+	 * Sets if the controls are going to be displayed
+	 *
+	 * @param controls
+	 *            if the controls are going to displayed
+	 */
+	public void setControls(Boolean controls)
+	{
+		this.controls = controls;
+	}
+
+	/**
+	 * The type of preload
+	 *
+	 * @see {@link #setPreload(Preload)}
+	 *
+	 * @return the preload
+	 */
+	public Preload getPreload()
+	{
+		return preload;
+	}
+
+	/**
+	 * Sets the type of preload.
+	 * <ul>
+	 * <li><b>none</b>: Hints to the user agent that either the author does not expect the user to
+	 * need the media resource, or that the server wants to minimise unnecessary traffic.</li>
+	 *
+	 * <li><b>metadata</b>: Hints to the user agent that the author does not expect the user to need
+	 * the media resource, but that fetching the resource metadata (dimensions, first frame, track
+	 * list, duration, etc) is reasonable.</li>
+	 *
+	 * <li><b>auto</b>: Hints to the user agent that the user agent can put the user's needs first
+	 * without risk to the server, up to and including optimistically downloading the entire
+	 * resource.</li>
+	 * </ul>
+	 * </p>
+	 *
+	 * @param preload
+	 *            the type of the preload
+	 */
+	public void setPreload(Preload preload)
+	{
+		this.preload = preload;
+	}
+
+	/**
+	 * Gets the position at which the media component starts the playback
+	 *
+	 * @see {@link #setStartTime(String)}
+	 *
+	 * @return the time at which position the media component starts the playback
+	 */
+	public String getStartTime()
+	{
+		return startTime;
+	}
+
+	/**
+	 * Sets the position at which the media component starts the playback<br>
+	 * <br>
+	 * t=<b>10</b>,20<br>
+	 * t=<b>npt:10</b>,20<br>
+	 * <br>
+	 *
+	 * t=<b>120s</b>,121.5s<br>
+	 * t=<b>npt:120</b>,0:02:01.5<br>
+	 * <br>
+	 *
+	 * t=<b>smpte-30:0:02:00</b>,0:02:01:15<br>
+	 * t=<b>smpte-25:0:02:00:00</b>,0:02:01:12.1<br>
+	 * <br>
+	 *
+	 * t=<b>clock:20090726T111901Z</b>,20090726T121901Z
+	 *
+	 * @param startTime
+	 *            the time at which position the media component starts the playback
+	 */
+	public void setStartTime(String startTime)
+	{
+		this.startTime = startTime;
+	}
+
+	/**
+	 * Gets the position at which the media component stops the playback
+	 *
+	 * @see {@link #setEndTime(String)}
+	 *
+	 * @return the time at which position the media component stops the playback
+	 */
+	public String getEndTime()
+	{
+		return endTime;
+	}
+
+	/**
+	 * Sets the position at which the media component stops the playback<br>
+	 * <br>
+	 * t=10,<b>20</b><br>
+	 * t=npt:10,<b>20</b><br>
+	 * <br>
+	 *
+	 * t=120s,<b>121.5s</b><br>
+	 * t=npt:120,<b>0:02:01.5</b><br>
+	 * <br>
+	 *
+	 * t=smpte-30:0:02:00,<b>0:02:01:15</b><br>
+	 * t=smpte-25:0:02:00:00,<b>0:02:01:12.1</b><br>
+	 * <br>
+	 *
+	 * t=clock:20090726T111901Z,<b>20090726T121901Z</b>
+	 *
+	 * @param endTime
+	 *            the time at which position the media component stops the playback
+	 */
+	public void setEndTime(String endTime)
+	{
+		this.endTime = endTime;
+	}
+
+	/**
+	 * Gets the media group.
+	 *
+	 * @return the media group
+	 */
+	public String getMediaGroup()
+	{
+		return mediaGroup;
+	}
+
+	/**
+	 * Sets the media group
+	 *
+	 * @param mediaGroup
+	 *            to be set
+	 */
+	public void setMediaGroup(String mediaGroup)
+	{
+		this.mediaGroup = mediaGroup;
+	}
+
+	/**
+	 * Gets the cross origin settings
+	 *
+	 * @see {@link #setCrossOrigin(Cors)}
+	 *
+	 * @return the cross origins settings
+	 */
+	public Cors getCrossOrigin()
+	{
+		return crossOrigin;
+	}
+
+	/**
+	 * Sets the cross origin settings<br>
+	 * <br>
+	 *
+	 * <b>ANONYMOUS</b>: Cross-origin CORS requests for the element will not have the credentials
+	 * flag set.<br>
+	 * <br>
+	 * <b>USER_CREDENTIALS</b>: Cross-origin CORS requests for the element will have the credentials
+	 * flag set.<br>
+	 * <br>
+	 * <b>NO_CORS</b>: The empty string is also a valid keyword, and maps to the Anonymous state.
+	 * The attribute's invalid value default is the Anonymous state. The missing value default, used
+	 * when the attribute is omitted, is the No CORS state
+	 *
+	 * @param crossOrigin
+	 *            the cross origins settings to set
+	 */
+	public void setCrossOrigin(Cors crossOrigin)
+	{
+		this.crossOrigin = crossOrigin;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/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
new file mode 100755
index 0000000..76a208f
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaStreamingResourceReference.java
@@ -0,0 +1,182 @@
+/*
+ * 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/499f11fd/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
new file mode 100644
index 0000000..da8abda
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/PartWriterCallback.java
@@ -0,0 +1,185 @@
+/*
+ * 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/499f11fd/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
new file mode 100755
index 0000000..76b4002
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java
@@ -0,0 +1,240 @@
+/*
+ * 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 org.apache.wicket.markup.ComponentTag;
+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;
+
+/**
+ * The source of an audio or a video media component
+ *
+ * @author Tobias Soloschenko
+ * @author Andrew Lombardi
+ */
+public class Source extends WebMarkupContainer
+{
+	private static final long serialVersionUID = 1L;
+
+	private boolean displayType;
+
+	private String type;
+
+	private String media;
+
+	private final MediaStreamingResourceReference mediaStreamingResourceReference;
+
+	private final PageParameters pageParameters;
+
+	private final String url;
+
+	public Source(String id)
+	{
+		this(id, null, null, null, null);
+	}
+
+	public Source(String id, IModel<?> model)
+	{
+		this(id, model, null, null, null);
+	}
+
+	public Source(String id, MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		this(id, null, null, null, mediaStreamingResourceReference);
+	}
+
+	public Source(String id, IModel<?> model,
+		MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		this(id, model, null, null, mediaStreamingResourceReference);
+	}
+
+	public Source(String id, MediaStreamingResourceReference mediaStreamingResourceReference,
+		PageParameters pageParameters)
+	{
+		this(id, null, null, pageParameters, mediaStreamingResourceReference);
+	}
+
+	public Source(String id, IModel<?> model,
+		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PageParameters pageParameters)
+	{
+		this(id, model, null, pageParameters, mediaStreamingResourceReference);
+	}
+
+	public Source(String id, String url)
+	{
+		this(id, null, url, null, null);
+	}
+
+	public Source(String id, IModel<?> model, String url)
+	{
+		this(id, model, url, null, null);
+	}
+
+	private Source(String id, IModel<?> model, String url, PageParameters pageParameters,
+	               MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		super(id, model);
+		this.url = url;
+		this.pageParameters = pageParameters;
+		this.mediaStreamingResourceReference = mediaStreamingResourceReference;
+	}
+
+	@Override
+	protected void onComponentTag(ComponentTag tag)
+	{
+		checkComponentTag(tag, "source");
+		super.onComponentTag(tag);
+
+		if (mediaStreamingResourceReference != null)
+		{
+			CharSequence url = RequestCycle.get().urlFor(mediaStreamingResourceReference, pageParameters);
+			tag.put("src", url);
+		} else if (url != null)
+		{
+			tag.put("src", url);
+		}
+
+		if (getDisplayType())
+		{
+			if (type != null)
+			{
+				tag.put("type", type);
+			}
+			else if (mediaStreamingResourceReference != null)
+			{
+				tag.put("type", mediaStreamingResourceReference.getType());
+			}
+		}
+
+		String _media = getMedia();
+		if (_media != null)
+		{
+			tag.put("media", _media);
+		}
+
+	}
+
+	/**
+	 * If the type is going to be displayed
+	 *
+	 * @return If the type is going to be displayed
+	 */
+	public boolean getDisplayType()
+	{
+		return displayType;
+	}
+
+	/**
+	 * Sets if the type is going to be displayed
+	 *
+	 * @param displayType
+	 *            if the type is going to be displayed
+	 */
+	public void setDisplayType(boolean displayType)
+	{
+		this.displayType = displayType;
+	}
+
+	/**
+	 * Gets the type
+	 *
+	 * @see {@link #setType(String)}
+	 *
+	 * @return the type of this media element
+	 */
+	public String getType()
+	{
+		return type;
+	}
+
+	/**
+	 * Sets the type<br>
+	 * <br>
+	 *
+	 * * The following list shows some examples of how to use the codecs= MIME parameter in the type
+	 * attribute.<br>
+	 * <br>
+	 *
+	 * H.264 Constrained baseline profile video (main and extended video compatible) level 3 and
+	 * Low-Complexity AAC audio in MP4 container<br>
+	 * &lt;source src='video.mp4' <b>type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'</b>&gt;<br>
+	 * H.264 Extended profile video (baseline-compatible) level 3 and Low-Complexity AAC audio in
+	 * MP4 container<br>
+	 * &lt;source src='video.mp4' <b>type='video/mp4; codecs="avc1.58A01E, mp4a.40.2"'</b>&gt;<br>
+	 * H.264 Main profile video level 3 and Low-Complexity AAC audio in MP4 container<br>
+	 * &lt;source src='video.mp4' <b>type='video/mp4; codecs="avc1.4D401E, mp4a.40.2"'</b>&gt;<br>
+	 * H.264 'High' profile video (incompatible with main, baseline, or extended profiles) level 3
+	 * and Low-Complexity AAC audio in MP4 container<br>
+	 * &lt;source src='video.mp4' <b>type='video/mp4; codecs="avc1.64001E, mp4a.40.2"'</b>&gt;<br>
+	 * MPEG-4 Visual Simple Profile Level 0 video and Low-Complexity AAC audio in MP4 container<br>
+	 * &lt;source src='video.mp4' <b>type='video/mp4; codecs="mp4v.20.8, mp4a.40.2"'</b>&gt;<br>
+	 * MPEG-4 Advanced Simple Profile Level 0 video and Low-Complexity AAC audio in MP4 container<br>
+	 * &lt;source src='video.mp4' <b>type='video/mp4; codecs="mp4v.20.240, mp4a.40.2"'</b>&gt;<br>
+	 * MPEG-4 Visual Simple Profile Level 0 video and AMR audio in 3GPP container<br>
+	 * &lt;source src='video.3gp' <b>type='video/3gpp; codecs="mp4v.20.8, samr"'</b>&gt;<br>
+	 * Theora video and Vorbis audio in Ogg container<br>
+	 * &lt;source src='video.ogv' <b>type='video/ogg; codecs="theora, vorbis"'</b>&gt;<br>
+	 * Theora video and Speex audio in Ogg container<br>
+	 * &lt;source src='video.ogv' <b>type='video/ogg; codecs="theora, speex"'</b>&gt;<br>
+	 * Vorbis audio alone in Ogg container<br>
+	 * &lt;source src='audio.ogg' <b>type='audio/ogg; codecs=vorbis'</b>&gt;<br>
+	 * Speex audio alone in Ogg container<br>
+	 * &lt;source src='audio.spx' <b>type='audio/ogg; codecs=speex'</b>&gt;<br>
+	 * FLAC audio alone in Ogg container<br>
+	 * &lt;source src='audio.oga' <b>type='audio/ogg; codecs=flac'</b>&gt;<br>
+	 * Dirac video and Vorbis audio in Ogg container<br>
+	 * &lt;source src='video.ogv' <b>type='video/ogg; codecs="dirac, vorbis"'</b>&gt;<br>
+	 * Theora video and Vorbis audio in Matroska container<br>
+	 * &lt;source src='video.mkv' <b>type='video/x-matroska; codecs="theora, vorbis"'</b>&gt;<br>
+	 *
+	 * @param type
+	 *            the type of this media element
+	 */
+	public void setType(String type)
+	{
+		this.type = type;
+	}
+
+	/**
+	 * The media for which the content of this source should be shown
+	 *
+	 * @See {@link #setMedia(String)}
+	 * @return The media for which the content of this source should be shown
+	 */
+	public String getMedia()
+	{
+		return media;
+	}
+
+	/**
+	 * Sets the media for which the content of this source should be shown<br>
+	 * <br>
+	 *
+	 * &lt;source src="movie.ogg" type="video/ogg" <b>media="screen and (min-width:320px)"&gt;<br>
+	 *
+	 * @param media
+	 *            the media for which to content of this source should be shown
+	 */
+	public void setMedia(String media)
+	{
+		this.media = media;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Track.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Track.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Track.java
new file mode 100755
index 0000000..c2554b3
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Track.java
@@ -0,0 +1,274 @@
+/*
+ * 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.util.Locale;
+
+import org.apache.wicket.markup.ComponentTag;
+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.ResourceReference;
+
+/**
+ * The track tag is used to provide subtitles, captions, descriptions, chapters, metadata to a video
+ * media component
+ *
+ * @author Tobias Soloschenko
+ */
+public class Track extends WebMarkupContainer
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * To be used for the kind attribute
+	 */
+	public enum Kind
+	{
+		SUBTITLES("subtitles"), CAPTIONS("captions"), DESCRIPTIONS("descriptions"), CHAPTERS(
+			"chapters"), METADATA("metadata");
+
+		private String realName;
+
+		Kind(String realName)
+		{
+			this.realName = realName;
+		}
+
+		public String getRealName()
+		{
+			return realName;
+		}
+	}
+
+	private Kind kind;
+
+	private String label;
+
+	private boolean defaultTrack;
+
+	private Locale srclang;
+
+	private final ResourceReference resourceReference;
+
+	private final String url;
+
+	private final PageParameters pageParameters;
+
+	public Track(String id)
+	{
+		this(id, null, null, null, null);
+	}
+
+	public Track(String id, IModel<?> model)
+	{
+		this(id, model, null, null, null);
+	}
+
+	public Track(String id, ResourceReference resourceReference)
+	{
+		this(id, null, null, null, resourceReference);
+	}
+
+	public Track(String id, IModel<?> model, ResourceReference resourceReference)
+	{
+		this(id, model, null, null, resourceReference);
+	}
+
+	public Track(String id, ResourceReference resourceReference, PageParameters pageParameters)
+	{
+		this(id, null, null, pageParameters, resourceReference);
+	}
+
+	public Track(String id, IModel<?> model, ResourceReference resourceReference,
+		PageParameters pageParameters)
+	{
+		this(id, model, null, pageParameters, resourceReference);
+	}
+
+	public Track(String id, String url)
+	{
+		this(id, null, url, null, null);
+	}
+
+	public Track(String id, IModel<?> model, String url)
+	{
+		this(id, model, url, null, null);
+	}
+
+	private Track(String id, IModel<?> model, String url, PageParameters pageParameters,
+		ResourceReference resourceReference)
+	{
+		super(id, model);
+		this.url = url;
+		this.pageParameters = pageParameters;
+		this.resourceReference = resourceReference;
+	}
+
+	@Override
+	protected void onComponentTag(ComponentTag tag)
+	{
+		checkComponentTag(tag, "track");
+		super.onComponentTag(tag);
+
+		if (resourceReference != null)
+		{
+			tag.put("src", RequestCycle.get().urlFor(resourceReference, pageParameters));
+		}
+		else if (url != null)
+		{
+			tag.put("src", url);
+		}
+
+		Kind _kind = getKind();
+		if (_kind != null)
+		{
+			tag.put("kind", _kind.getRealName());
+		}
+
+		String _label = getLabel();
+		if (_label != null)
+		{
+			tag.put("label", _label);
+		}
+
+		if (defaultTrack)
+		{
+			tag.put("default", "default");
+		}
+
+		// if the srclang field is set use this, else if the
+		// resource reference provides a locale use the language
+		// of the resource reference
+		Locale _srclang = getSrclang();
+		if (_srclang != null)
+		{
+			tag.put("srclang", _srclang.getLanguage());
+		}
+		else if (resourceReference != null && resourceReference.getLocale() != null)
+		{
+			tag.put("srclang", resourceReference.getLocale().getLanguage());
+		}
+	}
+
+	/**
+	 * Gets the kind of the track belongs to the media component
+	 *
+	 * @see {@link #setKind(Kind)}
+	 *
+	 * @return the kind
+	 */
+	public Kind getKind()
+	{
+		return kind;
+	}
+
+	/**
+	 * Sets the kind of the track belongs to the media component<br>
+	 * <br>
+	 * <b>SUBTITLES</b>: Transcription or translation of the dialogue, suitable for when the sound
+	 * is available but not understood (e.g. because the user does not understand the language of
+	 * the media resource's soundtrack). Displayed over the video.<br>
+	 * <br>
+	 * <b>CAPTIONS</b>: Transcription or translation of the dialogue, sound effects, relevant
+	 * musical cues, and other relevant audio information, suitable for when the soundtrack is
+	 * unavailable (e.g. because it is muted or because the user is deaf). Displayed over the video;
+	 * labeled as appropriate for the hard-of-hearing.<br>
+	 * <br>
+	 * <b>DESCRIPTIONS</b>: Textual descriptions of the video component of the media resource,
+	 * intended for audio synthesis when the visual component is unavailable (e.g. because the user
+	 * is interacting with the application without a screen while driving, or because the user is
+	 * blind). Synthesized as separate audio track.<br>
+	 * <br>
+	 * <b>CHAPTERS</b>: Chapter titles, intended to be used for navigating the media resource.
+	 * Displayed as an interactive list in the user agent's interface.<br>
+	 * <br>
+	 * <b>METADATA</b>: Tracks intended for use from script. Not displayed by the user agent.<br>
+	 * <br>
+	 *
+	 * @param kind
+	 *            the kind
+	 */
+	public void setKind(Kind kind)
+	{
+		this.kind = kind;
+	}
+
+	/**
+	 * The label for this track
+	 *
+	 * @return the label
+	 */
+	public String getLabel()
+	{
+		return label;
+	}
+
+	/**
+	 * Sets the label for this track
+	 *
+	 * @param label
+	 *            the label to be set
+	 */
+	public void setLabel(String label)
+	{
+		this.label = label;
+	}
+
+	/**
+	 * If the track is the default track
+	 *
+	 * @return if the track is the default track
+	 */
+	public boolean isDefaultTrack()
+	{
+		return defaultTrack;
+	}
+
+	/**
+	 * Sets if this track is the default track
+	 *
+	 * @param defaultTrack
+	 *            if the track is the default track
+	 */
+	public void setDefaultTrack(Boolean defaultTrack)
+	{
+		this.defaultTrack = defaultTrack;
+	}
+
+	/**
+	 * Gets the src lang
+	 *
+	 * @return the src lang
+	 */
+	public Locale getSrclang()
+	{
+		return srclang;
+	}
+
+	/**
+	 * Sets the src lang
+	 *
+	 * @param srclang
+	 *            the src lang to set
+	 */
+	public void setSrclang(Locale srclang)
+	{
+		this.srclang = srclang;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/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
new file mode 100755
index 0000000..e16cb8b
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java
@@ -0,0 +1,95 @@
+/*
+ * 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.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;
+
+/**
+ * An audio media component to playback audio files.
+ *
+ * @author Tobias Soloschenko
+ * @author Andrew Lombardi
+ */
+public class Audio extends MediaComponent
+{
+	private static final long serialVersionUID = 1L;
+
+	public Audio(String id)
+	{
+		super(id);
+	}
+
+	public Audio(String id, IModel<?> model)
+	{
+		super(id, model);
+	}
+
+	public Audio(String id, MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		super(id, mediaStreamingResourceReference);
+	}
+
+	public Audio(String id, IModel<?> model,
+		MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		super(id, model, mediaStreamingResourceReference);
+	}
+
+	public Audio(String id, MediaStreamingResourceReference mediaStreamingResourceReference,
+		PageParameters pageParameters)
+	{
+		super(id, mediaStreamingResourceReference, pageParameters);
+	}
+
+	public Audio(String id, IModel<?> model,
+		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PageParameters pageParameters)
+	{
+		super(id, model, mediaStreamingResourceReference, pageParameters);
+	}
+
+	public Audio(String id, String url)
+	{
+		super(id, url);
+	}
+
+	public Audio(String id, IModel<?> model, String url)
+	{
+		super(id, model, url);
+	}
+
+	public Audio(String id, String url, PageParameters pageParameters)
+	{
+		super(id, null, url, pageParameters);
+	}
+
+	public Audio(String id, IModel<?> model, String url, PageParameters pageParameters)
+	{
+		super(id, model, url, pageParameters);
+	}
+
+	@Override
+	protected void onComponentTag(ComponentTag tag)
+	{
+		checkComponentTag(tag, "audio");
+		super.onComponentTag(tag);
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/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
new file mode 100755
index 0000000..b73ba3d
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java
@@ -0,0 +1,210 @@
+/*
+ * 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.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.ResourceReference;
+
+/**
+ * A video media component to display videos.
+ *
+ * @author Tobias Soloschenko
+ * @author Andrew Lombardi
+ */
+public class Video extends MediaComponent
+{
+	private static final long serialVersionUID = 1L;
+
+	private Integer width;
+
+	private Integer height;
+
+	private ResourceReference poster;
+
+	private PageParameters posterPageParameters;
+
+	public Video(String id)
+	{
+		super(id);
+	}
+
+	public Video(String id, IModel<?> model)
+	{
+		super(id, model);
+	}
+
+	public Video(String id, MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		super(id, mediaStreamingResourceReference);
+	}
+
+	public Video(String id, IModel<?> model,
+		MediaStreamingResourceReference mediaStreamingResourceReference)
+	{
+		super(id, model, mediaStreamingResourceReference);
+	}
+
+	public Video(String id, MediaStreamingResourceReference mediaStreamingResourceReference,
+		PageParameters pageParameters)
+	{
+		super(id, mediaStreamingResourceReference, pageParameters);
+	}
+
+	public Video(String id, IModel<?> model,
+		MediaStreamingResourceReference mediaStreamingResourceReference,
+		PageParameters pageParameters)
+	{
+		super(id, model, mediaStreamingResourceReference, pageParameters);
+	}
+
+	public Video(String id, String url)
+	{
+		super(id, url);
+	}
+
+	public Video(String id, IModel<?> model, String url)
+	{
+		super(id, model, url);
+	}
+
+	public Video(String id, String url, PageParameters pageParameters)
+	{
+		super(id, null, url, pageParameters);
+	}
+
+	public Video(String id, IModel<?> model, String url, PageParameters pageParameters)
+	{
+		super(id, model, url, pageParameters);
+	}
+
+	@Override
+	protected void onComponentTag(ComponentTag tag)
+	{
+		checkComponentTag(tag, "video");
+		super.onComponentTag(tag);
+
+		Integer _width = getWidth();
+		if (_width != null)
+		{
+			tag.put("width", _width);
+		}
+
+		Integer _height = getHeight();
+		if (_height != null)
+		{
+			tag.put("height", _height);
+		}
+
+		ResourceReference _poster = getPoster();
+		if (_poster != null)
+		{
+			tag.put("poster", RequestCycle.get().urlFor(_poster, getPosterPageParameters()));
+		}
+	}
+
+	/**
+	 * The image to be displayed if the video isn't available
+	 *
+	 * @return the resource reference of the image
+	 */
+	public ResourceReference getPoster()
+	{
+		return poster;
+	}
+
+	/**
+	 * Gets the posters page parameters
+	 *
+	 * @return the page parameters for the poster
+	 */
+	public PageParameters getPosterPageParameters()
+	{
+		return posterPageParameters;
+	}
+
+	/**
+	 * Sets the image to be displayed if the video isn't available
+	 *
+	 * @param poster
+	 *            the resource reference of the image used if the video isn't available
+	 */
+	public void setPoster(ResourceReference poster)
+	{
+		this.poster = poster;
+	}
+
+	/**
+	 * Sets the image to be displayed if the video isn't available
+	 *
+	 * @param poster
+	 *            the resource reference of the image used if the video isn't available
+	 * @param posterPageParameters
+	 *            the page parameters for the poster
+	 */
+	public void setPoster(ResourceReference poster, PageParameters posterPageParameters)
+	{
+		this.poster = poster;
+		this.posterPageParameters = posterPageParameters;
+	}
+
+	/**
+	 * Gets the width of the video area
+	 *
+	 * @return the width of the video area
+	 */
+	public Integer getWidth()
+	{
+		return width;
+	}
+
+	/**
+	 * Sets the width of the video area
+	 *
+	 * @param width
+	 *            the width of the video area
+	 */
+	public void setWidth(Integer width)
+	{
+		this.width = width;
+	}
+
+	/**
+	 * Gets the height of the video area
+	 *
+	 * @return the height of the video area
+	 */
+	public Integer getHeight()
+	{
+		return height;
+	}
+
+	/**
+	 * Sets the height of the video area
+	 *
+	 * @param height
+	 *            the height of the video area
+	 */
+	public void setHeight(Integer height)
+	{
+		this.height = height;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaComponentsApplication.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaComponentsApplication.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaComponentsApplication.java
new file mode 100755
index 0000000..899f553
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaComponentsApplication.java
@@ -0,0 +1,52 @@
+/*
+ * 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 org.apache.wicket.Page;
+import org.apache.wicket.markup.html.IPackageResourceGuard;
+import org.apache.wicket.markup.html.SecurePackageResourceGuard;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.request.resource.caching.NoOpResourceCachingStrategy;
+
+public class MediaComponentsApplication extends WebApplication
+{
+	@Override
+	public Class<? extends Page> getHomePage()
+	{
+		return MediaTagsTestPage.class;
+	}
+
+	@Override
+	protected void init()
+	{
+		super.init();
+
+		getResourceSettings().setCachingStrategy(NoOpResourceCachingStrategy.INSTANCE);
+
+		IPackageResourceGuard packageResourceGuard = org.apache.wicket.Application.get()
+				.getResourceSettings()
+				.getPackageResourceGuard();
+		if (packageResourceGuard instanceof SecurePackageResourceGuard)
+		{
+			SecurePackageResourceGuard securePackageResourceGuard = (SecurePackageResourceGuard)packageResourceGuard;
+			securePackageResourceGuard.addPattern("+*.vtt");
+			securePackageResourceGuard.addPattern("+*.srt");
+			securePackageResourceGuard.addPattern("+*.mp3");
+			securePackageResourceGuard.addPattern("+*.m4a");
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.html
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.html b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.html
new file mode 100755
index 0000000..1f7d793
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>de.test.mediatags</title>
+	</head>
+	<body>
+		<video wicket:id="video">
+			<source wicket:id="source" />
+			<track wicket:id="track" />
+		</video>
+	</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/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
new file mode 100644
index 0000000..b191c35
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java
@@ -0,0 +1,53 @@
+/*
+ * 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.util.Locale;
+
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.media.Track.Kind;
+import org.apache.wicket.markup.html.media.video.Video;
+import org.apache.wicket.request.resource.PackageResourceReference;
+
+public class MediaTagsExtendedTestPage extends WebPage
+{
+	private static final long serialVersionUID = 1L;
+
+	public MediaTagsExtendedTestPage()
+	{
+		Video video = new Video("video", new MediaStreamingResourceReference(
+			MediaTagsTestPage.class, "dummyVideo.m4a"));
+
+		// source tag
+		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.setKind(Kind.SUBTITLES);
+		track.setLabel("Subtitles of video");
+		track.setSrclang(Locale.GERMANY);
+		track.setDefaultTrack(true);
+		video.add(track);
+
+		add(video);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTest.java
new file mode 100644
index 0000000..6698d79
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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 org.apache.wicket.WicketTestCase;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.util.tester.TagTester;
+import org.junit.Test;
+
+public class MediaTagsTest extends WicketTestCase
+{
+	@Override
+	protected WebApplication newApplication()
+	{
+		return new MediaComponentsApplication();
+	}
+
+	@Test
+	public void audioTagIsRenderedRight()
+	{
+		tester.startPage(MediaTagsTestPage.class);
+		String lastResponseAsString = tester.getLastResponse().getDocument();
+		TagTester createTagByAttribute = TagTester.createTagByAttribute(lastResponseAsString,
+			"audio");
+		assertTrue(createTagByAttribute.hasAttribute("autoplay"));
+		assertTrue(createTagByAttribute.hasAttribute("controls"));
+		assertTrue(createTagByAttribute.hasAttribute("loop"));
+		assertTrue(createTagByAttribute.hasAttribute("muted"));
+		assertEquals("user-credentials", createTagByAttribute.getAttribute("crossorigin"));
+		String attribute = createTagByAttribute.getAttribute("src");
+		assertTrue("The time period is set right in the src attribute",
+			attribute.contains("#t=5,10"));
+		assertTrue("page parameter is in the url of the src attribute",
+			attribute.contains("test=test"));
+	}
+
+	@Test
+	public void videoTagIsRenderedRight()
+	{
+		tester.startPage(MediaTagsTestPage.class);
+		String lastResponseAsString = tester.getLastResponse().getDocument();
+		TagTester createTagByAttribute = TagTester.createTagByAttribute(lastResponseAsString,
+			"video");
+		String attribute = createTagByAttribute.getAttribute("poster");
+		assertTrue("page parameter is in the url of the poster",
+			attribute.contains("test2=test2"));
+		String attributesrc = createTagByAttribute.getAttribute("src");
+		assertTrue("video url is in the src attribute",
+			attributesrc.contains("dummyVideo.m4a"));
+		assertEquals("500", createTagByAttribute.getAttribute("width"));
+		assertEquals("400", createTagByAttribute.getAttribute("height"));
+	}
+
+	@Test
+	public void extendedVideoTagIsRenderedRight()
+	{
+		tester.startPage(MediaTagsExtendedTestPage.class);
+		String lastResponseAsString = tester.getLastResponse().getDocument();
+		TagTester createTagByAttribute = TagTester.createTagByAttribute(lastResponseAsString,
+			"video");
+		assertTrue(createTagByAttribute.hasChildTag("source"));
+		assertTrue(createTagByAttribute.hasChildTag("track"));
+
+
+		TagTester sourceTag = TagTester.createTagByAttribute(lastResponseAsString, "source");
+		assertEquals("video/mp4", sourceTag.getAttribute("type"));
+		assertEquals("screen and (device-width:500px)", sourceTag.getAttribute("media"));
+		assertEquals("http://www.mytestpage.xc/video.m4a", sourceTag.getAttribute("src"));
+
+		TagTester trackTag = TagTester.createTagByAttribute(lastResponseAsString, "track");
+
+		assertTrue(trackTag.getAttribute("src").contains("dummySubtitles"));
+		assertEquals("subtitles", trackTag.getAttribute("kind"));
+		assertEquals("Subtitles of video", trackTag.getAttribute("label"));
+		assertEquals("default", trackTag.getAttribute("default"));
+		assertEquals("de", trackTag.getAttribute("srclang"));
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.html
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.html b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.html
new file mode 100755
index 0000000..e68360d
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>de.test.mediatags</title>
+	</head>
+	<body>
+		<audio wicket:id="audio" />
+		<video wicket:id="video"/>
+	</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/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
new file mode 100644
index 0000000..e8dbdd8
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java
@@ -0,0 +1,56 @@
+/*
+ * 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 org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.media.MediaComponent.Cors;
+import org.apache.wicket.markup.html.media.audio.Audio;
+import org.apache.wicket.markup.html.media.video.Video;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.PackageResourceReference;
+
+public class MediaTagsTestPage extends WebPage
+{
+	private static final long serialVersionUID = 1L;
+
+	public MediaTagsTestPage()
+	{
+		PageParameters pageParameters = new PageParameters();
+		pageParameters.set("test", "test");
+		Audio audio = new Audio("audio", new MediaStreamingResourceReference(
+			MediaTagsTestPage.class, "dummyAudio.mp3"), pageParameters);
+		audio.setAutoplay(true);
+		audio.setControls(true);
+		audio.setCrossOrigin(Cors.USER_CREDENTIALS);
+		audio.setLooping(true);
+		audio.setMuted(true);
+		audio.setStartTime("5");
+		audio.setEndTime("10");
+		add(audio);
+
+		Video video = new Video("video", new MediaStreamingResourceReference(
+			MediaTagsTestPage.class, "dummyVideo.m4a"));
+		PageParameters pageParameters2 = new PageParameters();
+		pageParameters2.add("test2", "test2");
+		video.setPoster(new PackageResourceReference(MediaTagsTestPage.class, "dummyPoster.jpg"),
+			pageParameters2);
+		video.setWidth(500);
+		video.setHeight(400);
+		add(video);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummyAudio.mp3
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummyAudio.mp3 b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummyAudio.mp3
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummyPoster.jpg
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummyPoster.jpg b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummyPoster.jpg
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummySubtitles.vtt
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummySubtitles.vtt b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummySubtitles.vtt
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummyVideo.m4a
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummyVideo.m4a b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/dummyVideo.m4a
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-examples/pom.xml
----------------------------------------------------------------------
diff --git a/wicket-examples/pom.xml b/wicket-examples/pom.xml
index d03ffad..c184b27 100644
--- a/wicket-examples/pom.xml
+++ b/wicket-examples/pom.xml
@@ -204,6 +204,7 @@
 					<include>**/*.txt</include>
 					<include>**/*.xml</include>
 					<include>**/*.properties</include>
+					<include>**/*.mp4</include>
 				</includes>
 			</resource>
 			<resource>
@@ -221,6 +222,7 @@
 					<include>**/*.properties</include>
 					<include>**/*.vm</include>
 					<include>**/*.tmpl</include>
+					<include>**/*.mp4</include>
 				</includes>
 			</resource>
 		</resources>

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.css
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.css b/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.css
new file mode 100644
index 0000000..45dee80
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.css
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+video{
+	float:left;
+	height:240px;
+	width:320px;
+	margin-bottom:10px;
+	margin-right:10px;
+}
+.videoDescription{
+	float:left;
+	height:240px;
+	width:300px;
+	margin-bottom:10px;
+}
+.clearer{
+	clear:both;
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.html
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.html b/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.html
new file mode 100644
index 0000000..be0b1ce
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.html
@@ -0,0 +1,29 @@
+<html xmlns:wicket="http://wicket.apache.org">
+<head>
+    <title>Wicket Examples - videos</title>
+    <link rel="stylesheet" type="text/css" href="style.css"/>
+</head>
+<body>
+	<div wicket:id="mainNavigation"></div>
+	<h2>Below there are three types of videos to show the basic functionality of Wicket's media implementation</h2>
+	<h3>The implementation features full support of video / audio / source / track tags.</h3>
+	<div>
+		<!-- Video 1 -->
+		<video wicket:id="video1" ></video>
+		<div class="videoDescription"><b>Video1</b><br/>Demonstrate the basic set of methods to configure a video (setAutoplay(false); setControls(true); setLooping(false); setWidth(320); setHeight(240);) The width and height are null by default which means that the video is going to be rendered in size of the media file.</div>
+		<div class="clearer"></div>
+
+		<!-- Video 2 -->
+		<video wicket:id="video2" >
+			<source wicket:id="source2" />
+		</video>
+		<div class="videoDescription"><b>Video2</b><br/>This video is rendered with a source tag. You can add many source tags and apply a media query (setMedia(query);) so that based on the resolution different videos are going to be displayed.</div>
+		<div class="clearer"></div>
+
+		<!-- Video 3 -->
+		<video wicket:id="video3" ></video>
+		<div class="videoDescription"><b>Video3</b><br/>The last sample shows a remote video located here: http://media.w3.org/2010/05/video/movie_300.mp4. It can be added by set the url as String instead of adding a ResourceReference.</div>
+		<div class="clearer"></div>
+	</div>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/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
new file mode 100644
index 0000000..87d70f5
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java
@@ -0,0 +1,116 @@
+/*
+ * 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.examples.media;
+
+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;
+import org.apache.wicket.request.resource.PackageResourceReference;
+
+
+/**
+ * Demonstrates different flavors of org.apache.wicket.examples.videos.<br>
+ * <br>
+ *
+ * Videos are from: http://media.w3.org/2010/05/video/<br>
+ * <br>
+ * Images are from: http://search.creativecommons.org/ with check on commercial use and modify...
+ *
+ * @author Tobias Soloschenko
+ */
+public final class Home extends WicketExamplePage
+{
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Constructor
+	 */
+	public Home()
+	{
+		// Internal video with several options
+
+		Video video1 = new Video("video1", new MediaStreamingResourceReference(Home.class,
+			"video.mp4"));
+		video1.setAutoplay(false);
+		video1.setControls(true);
+		video1.setLooping(false);
+		video1.setWidth(320);
+		video1.setHeight(240);
+		video1.setPoster(new PackageResourceReference(Home.class, "novideo.gif"));
+		add(video1);
+
+		// video with source
+
+		Video video2 = new Video("video2");
+		video2.setPoster(new PackageResourceReference(Home.class, "novideo.gif"));
+
+		Source source2 = new Source("source2", new MediaStreamingResourceReference(Home.class,
+			"video.mp4"));
+		// Need to be set to true to show the type
+		source2.setDisplayType(true);
+		// the default type is the mime type of the image with no codec information
+		source2.setType("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"");
+		video2.add(source2);
+
+		add(video2);
+
+		// External video
+		PageParameters pageParameters = new PageParameters();
+		pageParameters.add("random", UUID.randomUUID().toString());
+		pageParameters.add("test", "test");
+		Video video3 = new Video("video3", "http://media.w3.org/2010/05/video/movie_300.mp4",
+			pageParameters);
+		video3.setPoster(new PackageResourceReference(Home.class, "novideo.gif"));
+		add(video3);
+
+		/*
+		 * // video with track
+		 * Video video4 = new Video("video4", new MediaStreamingResourceReference(Home.class, "dummyVideo.m4a"));
+		 *
+		 * // source tag
+		 * Source source4 = new Source("source4", "http://www.mytestpage.xc/video.m4a");
+		 * source4.setMedia("screen and (device-width:500px)");
+		 * source4.setType("video/mp4");
+		 * source4.setDisplayType(true); video4.add(source4);
+		 *
+		 * // tack tag
+		 * Track track4 = new Track("track4", new PackageResourceReference(Home.class, "dummySubtitles.vtt"));
+		 * track4.setKind(Kind.subtitles);
+		 * track4.setLabel("Subtitles of video");
+		 * track4.setSrclang(Locale.GERMANY);
+		 * track4.setDefaultTrack(true);
+		 * video4.add(track4);
+		 *
+		 * add(video4);
+		 */
+	}
+
+	@Override
+	public void renderHead(IHeaderResponse response)
+	{
+		super.renderHead(response);
+		response.render(CssHeaderItem.forReference(new PackageResourceReference(Home.class,
+			"Home.css")));
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-examples/src/main/java/org/apache/wicket/examples/media/VideosApplication.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/media/VideosApplication.java b/wicket-examples/src/main/java/org/apache/wicket/examples/media/VideosApplication.java
new file mode 100644
index 0000000..2e59703
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/media/VideosApplication.java
@@ -0,0 +1,63 @@
+/*
+ * 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.examples.media;
+
+import org.apache.wicket.Page;
+import org.apache.wicket.examples.WicketExampleApplication;
+import org.apache.wicket.markup.html.IPackageResourceGuard;
+import org.apache.wicket.markup.html.SecurePackageResourceGuard;
+
+
+/**
+ * Application class for the videos examples.
+ *
+ * @author Tobias Soloschenko
+ */
+public class VideosApplication extends WicketExampleApplication
+{
+	/**
+	 * Constructor
+	 */
+	public VideosApplication()
+	{
+
+	}
+
+	/**
+	 * @see org.apache.wicket.Application#getHomePage()
+	 */
+	@Override
+	public Class<? extends Page> getHomePage()
+	{
+		return Home.class;
+	}
+
+	/**
+	 * @see org.apache.wicket.examples.WicketExampleApplication#init()
+	 */
+	@Override
+	protected void init()
+	{
+		IPackageResourceGuard packageResourceGuard = getResourceSettings().getPackageResourceGuard();
+		if (packageResourceGuard instanceof SecurePackageResourceGuard)
+		{
+			SecurePackageResourceGuard guard = (SecurePackageResourceGuard)packageResourceGuard;
+			guard.addPattern("+*.mp4");
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-examples/src/main/java/org/apache/wicket/examples/media/novideo.gif
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/media/novideo.gif b/wicket-examples/src/main/java/org/apache/wicket/examples/media/novideo.gif
new file mode 100644
index 0000000..98cc51a
Binary files /dev/null and b/wicket-examples/src/main/java/org/apache/wicket/examples/media/novideo.gif differ

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-examples/src/main/java/org/apache/wicket/examples/media/video.mp4
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/media/video.mp4 b/wicket-examples/src/main/java/org/apache/wicket/examples/media/video.mp4
new file mode 100644
index 0000000..cf59777
Binary files /dev/null and b/wicket-examples/src/main/java/org/apache/wicket/examples/media/video.mp4 differ

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-examples/src/main/resources/org/apache/wicket/examples/homepage/HomePage.html
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/resources/org/apache/wicket/examples/homepage/HomePage.html b/wicket-examples/src/main/resources/org/apache/wicket/examples/homepage/HomePage.html
index 4789751..d83ae2c 100644
--- a/wicket-examples/src/main/resources/org/apache/wicket/examples/homepage/HomePage.html
+++ b/wicket-examples/src/main/resources/org/apache/wicket/examples/homepage/HomePage.html
@@ -66,6 +66,7 @@
 		<tr><td align="right"><a href="atmosphere">Atmosphere</a></td><td> - Atmosphere Framework integration</td></tr>
 		<tr><td align="right"><a href="cdi">CDI</a></td><td> - Context Dependency and Injection integration</td></tr>
 		<tr><td align="right"><a href="bean-validation">Bean Validation</a></td><td> - Bean Validation integration (JSR 303)</td></tr>
+        <tr><td align="right"><a href="videos">HTML5 Media Tags</a></td><td> - Integration for HTML5 Media Tags</td></tr>
     	</tbody>
 		</table>
 	</div>

http://git-wip-us.apache.org/repos/asf/wicket/blob/499f11fd/wicket-examples/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/webapp/WEB-INF/web.xml b/wicket-examples/src/main/webapp/WEB-INF/web.xml
index 92f956a..ec23694 100644
--- a/wicket-examples/src/main/webapp/WEB-INF/web.xml
+++ b/wicket-examples/src/main/webapp/WEB-INF/web.xml
@@ -16,9 +16,9 @@
    limitations under the License.
 -->
 <web-app xmlns="http://java.sun.com/xml/ns/javaee"
-		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-		 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
-		 version="2.5">
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
 
 	<display-name>Wicket Examples</display-name>
 
@@ -162,6 +162,15 @@
 	</filter>
 
 	<filter>
+		<filter-name>VideosApplication</filter-name>
+		<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
+		<init-param>
+		  <param-name>applicationClassName</param-name>
+		  <param-value>org.apache.wicket.examples.media.VideosApplication</param-value>
+		</init-param>
+	</filter>
+
+	<filter>
 		<filter-name>LibraryApplication</filter-name>
 		<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
 		<init-param>
@@ -544,6 +553,13 @@
 	</filter-mapping>
 
 	<filter-mapping>
+		<filter-name>VideosApplication</filter-name>
+		<url-pattern>/videos/*</url-pattern>
+		<dispatcher>REQUEST</dispatcher>
+		<dispatcher>INCLUDE</dispatcher>
+	</filter-mapping>
+
+	<filter-mapping>
 		<filter-name>HelloWorldApplication</filter-name>
 		<url-pattern>/helloworld/*</url-pattern>
 		<dispatcher>REQUEST</dispatcher>


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

Posted by mg...@apache.org.
WICKET-5819 Support for HTML 5 media tags (audio / video)

Simplify PartWriterCallback by using BoundedInputStream from commons-io.
Add an additional test case with bigger range sizes


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

Branch: refs/heads/master
Commit: a1d0df298447247fa901cdee26caf6ae4a727c2d
Parents: 4a36aa7
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Fri Mar 27 21:25:17 2015 +0200
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Mon Mar 30 21:59:15 2015 +0300

----------------------------------------------------------------------
 .../request/resource/PartWriterCallback.java    | 79 +++++++++++++-------
 .../resource/PackageResourceReferenceTest.java  | 56 ++++++++++++++
 .../request/resource/resource_gt_4096.txt       |  1 +
 3 files changed, 108 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/a1d0df29/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
index c0c7a63..5fe8185 100644
--- 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
@@ -20,28 +20,47 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import org.apache.commons.io.input.BoundedInputStream;
 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.IOUtils;
 import org.apache.wicket.util.io.Streams;
+import org.apache.wicket.util.lang.Args;
 
 /**
  * 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.
+ * from attributes in {@link #writeData(org.apache.wicket.request.resource.IResource.Attributes)}  method.
  *
  * @author Tobias Soloschenko
- *
  */
 public class PartWriterCallback extends WriteCallback
 {
+	/**
+	 * The input stream to read from
+	 */
 	private final InputStream inputStream;
 
+	/**
+	 * The total length to read if {@link #endbyte} is not specified
+	 */
 	private final Long contentLength;
 
-	private final Long startbyte;
+	/**
+	 * The byte to start reading from. If omitted then the input stream will be read
+	 * from its beginning
+	 */
+	private Long startbyte;
 
+	/**
+	 * The end byte to read from the {@link #inputStream}.
+	 * If omitted then the input stream will be read till its end
+	 */
 	private Long endbyte;
 
+	/**
+	 * The size of the buffer that is used for the copying of the data
+	 */
 	private int bufferSize;
 
 
@@ -51,12 +70,12 @@ public class PartWriterCallback extends WriteCallback
 	 * 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.
+	 * stream is copied.
 	 *
 	 * @param inputStream
-	 *            the input stream to be read
-	 * @param the
-	 *            content length
+	 *            the input stream to read from
+	 * @param contentLength
+	 *            content length of the input stream. Ignored if <em>endByte</em> is specified
 	 * @param startbyte
 	 *            the start position to read from (if not null the number of bytes are skipped till
 	 *            the stream is read)
@@ -68,7 +87,7 @@ public class PartWriterCallback extends WriteCallback
 		Long endbyte)
 	{
 		this.inputStream = inputStream;
-		this.contentLength = contentLength;
+		this.contentLength = Args.notNull(contentLength, "contentLength");
 		this.startbyte = startbyte;
 		this.endbyte = endbyte;
 	}
@@ -97,6 +116,12 @@ public class PartWriterCallback extends WriteCallback
 				{
 					inputStream.skip(startbyte);
 				}
+				else
+				{
+					// If no start byte has been given set it to 0
+					// which means no bytes has been skipped
+					startbyte = 0L;
+				}
 
 				// If there are no end bytes given read the whole stream till the end
 				if (endbyte == null || Long.valueOf(-1).equals(endbyte))
@@ -104,36 +129,34 @@ public class PartWriterCallback extends WriteCallback
 					endbyte = contentLength;
 				}
 
-				// The read bytes in the current buffer
-				int readBytes;
+				BoundedInputStream boundedInputStream = null;
+				try
+				{
+					// Stream is going to be read from the starting point next to the skipped bytes
+					// till the end byte computed by the range between startbyte / endbyte
+					boundedInputStream = new BoundedInputStream(inputStream, endbyte - startbyte);
 
-				// The total bytes read
-				long totalBytes = 0;
+					// The original input stream is going to be closed by the end of the request
+					// so set propagate close to false
+					boundedInputStream.setPropagateClose(false);
 
-				while ((readBytes = inputStream.read(buffer)) != -1)
-				{
-					totalBytes += readBytes;
+					// The read bytes in the current buffer
+					int readBytes;
 
-					// Check if the end byte is reached
-					if (endbyte - totalBytes < 0)
+					while ((readBytes = boundedInputStream.read(buffer)) != -1)
 					{
-						// calculate the bytes left to be read in the current buffer
-						// can be casted to int, because the the previous chunks are
-						// subtracted - so it can't exceed buffer size
-						int leftBytesToBeRead = (int)(totalBytes - startbyte) -
-							(int)(totalBytes - endbyte);
-						outputStream.write(buffer, 0, leftBytesToBeRead);
-						break;
-					}
-					else
-					{
-						// If the end byte is not reached read the full buffer
 						outputStream.write(buffer, 0, readBytes);
 					}
 				}
+				finally
+				{
+					IOUtils.closeQuietly(boundedInputStream);
+				}
 			}
 			else
 			{
+				// No range has been given so copy the content
+				// from input stream to the output stream
 				Streams.copy(inputStream, outputStream, getBufferSize());
 			}
 		}

http://git-wip-us.apache.org/repos/asf/wicket/blob/a1d0df29/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java b/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
index a58a2c9..4057674 100644
--- a/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.wicket.request.resource;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.Locale;
 
 import org.apache.wicket.Application;
@@ -28,6 +30,7 @@ import org.apache.wicket.request.resource.AbstractResource.ContentRangeType;
 import org.apache.wicket.request.resource.IResource.Attributes;
 import org.apache.wicket.request.resource.ResourceReference.UrlAttributes;
 import org.apache.wicket.response.ByteArrayResponse;
+import org.apache.wicket.util.io.IOUtils;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -275,6 +278,59 @@ public class PackageResourceReferenceTest extends WicketTestCase
 
 	/**
 	 * See WICKET-5819 - Media tags
+	 *
+	 * @throws IOException
+	 */
+	@Test
+	public void testContentRangeLarge() throws IOException
+	{
+		InputStream resourceAsStream = null;
+		try
+		{
+			resourceAsStream = PackageResourceReference.class.getResourceAsStream("resource_gt_4096.txt");
+			String content = new String(IOUtils.toByteArray(resourceAsStream));
+
+			// Check buffer comprehensive range request
+			String bytes4094_4106 = makeRangeRequestToBigResource("bytes=4094-4106");
+			assertEquals(12, bytes4094_4106.length());
+			assertEquals("River Roller", bytes4094_4106);
+
+			// Check buffer exceeding range request
+			String bytes1000_5000 = makeRangeRequestToBigResource("bytes=1000-5000");
+			assertEquals(4000, bytes1000_5000.length());
+			assertEquals(content.substring(1000, 5000), bytes1000_5000);
+
+			// Check buffer exceeding range request until end of content
+			String bytes1000_end = makeRangeRequestToBigResource("bytes=1000-");
+			assertEquals(4529, bytes1000_end.length());
+			assertEquals(content.substring(1000, content.length()), bytes1000_end);
+
+			// Check complete range request
+			assertEquals(content.length(), makeRangeRequestToBigResource("bytes=-").length());
+		}
+		finally
+		{
+			IOUtils.closeQuietly(resourceAsStream);
+		}
+	}
+
+	private String makeRangeRequestToBigResource(String range)
+	{
+		ResourceReference reference = new PackageResourceReference(scope, "resource_gt_4096.txt",
+			null, null, null);
+
+		ByteArrayResponse byteResponse = new ByteArrayResponse();
+
+		Request request = tester.getRequestCycle().getRequest();
+		MockHttpServletRequest mockHttpServletRequest = (MockHttpServletRequest)request.getContainerRequest();
+		mockHttpServletRequest.setHeader("range", range);
+		Attributes mockAttributes = new Attributes(request, byteResponse);
+		reference.getResource().respond(mockAttributes);
+		return new String(byteResponse.getBytes());
+	}
+
+	/**
+	 * See WICKET-5819 - Media tags
 	 */
 	@Test
 	public void testContentRangeHeaders()

http://git-wip-us.apache.org/repos/asf/wicket/blob/a1d0df29/wicket-core/src/test/java/org/apache/wicket/request/resource/resource_gt_4096.txt
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/request/resource/resource_gt_4096.txt b/wicket-core/src/test/java/org/apache/wicket/request/resource/resource_gt_4096.txt
new file mode 100644
index 0000000..2b0c235
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/request/resource/resource_gt_4096.txt
@@ -0,0 +1 @@
+HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout ManifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate Workbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave River Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Subversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany 
 UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout ManifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate Workbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave River Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Su
 bversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout ManifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate Workbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave Riv
 er Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Subversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout ManifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate W
 orkbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave River Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Subversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout Ma
 nifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate Workbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave River Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Subversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper
\ No newline at end of file


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

Posted by mg...@apache.org.
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/>