Index: src/wp-admin/css/customize-controls.css
===================================================================
--- src/wp-admin/css/customize-controls.css	(revision 38842)
+++ src/wp-admin/css/customize-controls.css	(working copy)
@@ -555,6 +555,7 @@
 	display: block;
 	font-style: italic;
 	line-height: 18px;
+	margin-top: 0;
 	margin-bottom: 5px;
 }
 
Index: src/wp-content/themes/twentyfourteen/functions.php
===================================================================
--- src/wp-content/themes/twentyfourteen/functions.php	(revision 38842)
+++ src/wp-content/themes/twentyfourteen/functions.php	(working copy)
@@ -409,7 +409,7 @@
 		$classes[] = 'group-blog';
 	}
 
-	if ( get_header_image() ) {
+	if ( get_header_image() || function_exists( 'has_header_video' ) && has_header_video() ) {
 		$classes[] = 'header-image';
 	} elseif ( ! in_array( $GLOBALS['pagenow'], array( 'wp-activate.php', 'wp-signup.php' ) ) ) {
 		$classes[] = 'masthead-fixed';
Index: src/wp-content/themes/twentyfourteen/header.php
===================================================================
--- src/wp-content/themes/twentyfourteen/header.php	(revision 38842)
+++ src/wp-content/themes/twentyfourteen/header.php	(working copy)
@@ -32,7 +32,11 @@
 
 <body <?php body_class(); ?>>
 <div id="page" class="hfeed site">
-	<?php if ( get_header_image() ) : ?>
+	<?php if ( function_exists( 'has_header_video' ) && has_header_video() ) : ?>
+		<div id="site-header">
+			<?php the_header_video_tag(); ?>
+		</div>
+	<?php elseif ( get_header_image() ) : ?>
 	<div id="site-header">
 		<a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home">
 			<img src="<?php header_image(); ?>" width="<?php echo get_custom_header()->width; ?>" height="<?php echo get_custom_header()->height; ?>" alt="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>">
Index: src/wp-content/themes/twentyfourteen/inc/custom-header.php
===================================================================
--- src/wp-content/themes/twentyfourteen/inc/custom-header.php	(revision 38842)
+++ src/wp-content/themes/twentyfourteen/inc/custom-header.php	(working copy)
@@ -40,6 +40,7 @@
 		'width'                  => 1260,
 		'height'                 => 240,
 		'flex-height'            => true,
+		'video'                  => true,
 		'wp-head-callback'       => 'twentyfourteen_header_style',
 		'admin-head-callback'    => 'twentyfourteen_admin_header_style',
 		'admin-preview-callback' => 'twentyfourteen_admin_header_image',
Index: src/wp-content/themes/twentyfourteen/js/functions.js
===================================================================
--- src/wp-content/themes/twentyfourteen/js/functions.js	(revision 38842)
+++ src/wp-content/themes/twentyfourteen/js/functions.js	(working copy)
@@ -103,7 +103,7 @@
 
 			if ( body.is( '.header-image' ) ) {
 				toolbarOffset  = body.is( '.admin-bar' ) ? $( '#wpadminbar' ).height() : 0;
-				mastheadOffset = $( '#masthead' ).offset().top - toolbarOffset;
+				mastheadOffset = $( '#site-header' ).height() - toolbarOffset;
 
 				_window.on( 'scroll.twentyfourteen', function() {
 					if ( _window.scrollTop() > mastheadOffset && mastheadHeight < 49 ) {
@@ -112,6 +112,13 @@
 						body.removeClass( 'masthead-fixed' );
 					}
 				} );
+
+				// Update masthead offset after a selective refresh.
+				if ( 'undefined' !== typeof wp && wp.customize && wp.customize.selectiveRefresh ) {
+					wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
+						mastheadOffset = $( '#site-header' ).height() - toolbarOffset;
+					} );
+				}
 			}
 		}
 
Index: src/wp-content/themes/twentyfourteen/style.css
===================================================================
--- src/wp-content/themes/twentyfourteen/style.css	(revision 38842)
+++ src/wp-content/themes/twentyfourteen/style.css	(working copy)
@@ -88,6 +88,7 @@
 video {
 	display: inline-block;
 	max-width: 100%;
+	height: auto;
 }
 
 html {
@@ -608,6 +609,10 @@
 	max-width: 100%;
 }
 
+#wp-custom-header-video video {
+	margin-bottom: -8px;
+}
+
 p > embed,
 p > iframe,
 p > object,
Index: src/wp-content/themes/twentyseventeen/components/header/header-image.php
===================================================================
--- src/wp-content/themes/twentyseventeen/components/header/header-image.php	(revision 38842)
+++ src/wp-content/themes/twentyseventeen/components/header/header-image.php	(working copy)
@@ -11,6 +11,7 @@
 ?>
 <div class="custom-header">
 	<?php
+	the_custom_header();
 	$header_image = get_header_image();
 
 	// Check if Custom Header image has been added.
Index: src/wp-content/themes/twentyseventeen/inc/custom-header.php
===================================================================
--- src/wp-content/themes/twentyseventeen/inc/custom-header.php	(revision 38842)
+++ src/wp-content/themes/twentyseventeen/inc/custom-header.php	(working copy)
@@ -21,6 +21,7 @@
 		'width'              => 2000,
 		'height'             => 1200,
 		'flex-height'        => true,
+		'video'              => true,
 		'wp-head-callback'   => 'twentyseventeen_header_style',
 	) ) );
 
Index: src/wp-content/themes/twentyseventeen/style.css
===================================================================
--- src/wp-content/themes/twentyseventeen/style.css	(revision 38842)
+++ src/wp-content/themes/twentyseventeen/style.css	(working copy)
@@ -2634,7 +2634,8 @@
 # Media
 --------------------------------------------------------------*/
 
-img {
+img,
+video {
 	height: auto; /* Make sure images are scaled correctly. */
 	max-width: 100%; /* Adhere to container width. */
 }
Index: src/wp-includes/class-wp-customize-manager.php
===================================================================
--- src/wp-includes/class-wp-customize-manager.php	(revision 38842)
+++ src/wp-includes/class-wp-customize-manager.php	(working copy)
@@ -3223,12 +3223,46 @@
 
 		/* Custom Header */
 
+		if ( current_theme_supports( 'custom-header', 'video' ) ) {
+			$title = __( 'Header Visuals' );
+			$description = __( 'If you add a video, the image will be used as a fallback while the video loads.' );
+			$width = absint( get_theme_support( 'custom-header', 'width' ) );
+			$height = absint( get_theme_support( 'custom-header', 'height' ) );
+			if ( $width && $height ) {
+				/* translators: %s: header size in pixels */
+				$control_description = sprintf( __( 'Upload your video in <code>.mp4</code> format and minimize its file size for best results. Your theme recommends dimensions of %s pixels.' ),
+					sprintf( '<strong>%s &times; %s</strong>', $width, $height )
+				);
+			} elseif ( $width ) {
+				/* translators: %s: header width in pixels */
+				$control_description = sprintf( __( 'Upload your video in <code>.mp4</code> format and minimize its file size for best results. Your theme recommends a width of %s pixels.' ),
+					sprintf( '<strong>%s</strong>', $width )
+				);
+			} else {
+				/* translators: %s: header height in pixels */
+				$control_description = sprintf( __( 'Upload your video in <code>.mp4</code> format and minimize its file size for best results. Your theme recommends a height of %s pixels.' ),
+					sprintf( '<strong>%s</strong>', $height )
+				);
+			}
+		} else {
+			$title = __( 'Header Image' );
+			$description = '';
+			$control_description = '';
+		}
+
 		$this->add_section( 'header_image', array(
-			'title'          => __( 'Header Image' ),
+			'title'          => $title,
+			'description'    => $description,
 			'theme_supports' => 'custom-header',
 			'priority'       => 60,
 		) );
 
+		$this->add_setting( 'header_video', array(
+			'theme_supports'    => array( 'custom-header', 'video' ),
+			'transport'         => 'postMessage',
+			'validate_callback' => array( $this, '_validate_header_video' ),
+		) );
+
 		$this->add_setting( new WP_Customize_Filter_Setting( $this, 'header_image', array(
 			'default'        => get_theme_support( 'custom-header', 'default-image' ),
 			'theme_supports' => 'custom-header',
@@ -3239,8 +3273,22 @@
 			'theme_supports' => 'custom-header',
 		) ) );
 
+		$this->add_control( new WP_Customize_Media_Control( $this, 'header_video', array(
+			'theme_supports' => array( 'custom-header', 'video' ),
+			'label'          => __( 'Header Video' ),
+			'description'    => $control_description,
+			'section'        => 'header_image',
+			'mime_type'      => 'video',
+		) ) );
+
 		$this->add_control( new WP_Customize_Header_Image_Control( $this ) );
 
+		$this->selective_refresh->add_partial( 'header_video', array(
+			'selector' => '#wp-custom-header-video',
+			'render_callback' => 'get_header_video_tag',
+			'settings' => array( 'header_video', 'header_image' ), // The image is used as a video fallback here.
+		) );
+
 		/* Custom Background */
 
 		$this->add_section( 'background_image', array(
@@ -3595,6 +3643,27 @@
 	}
 
 	/**
+	 * Callback for validating the header_video value.
+	 *
+	 * Ensures that the selected video is less than 8MB and provides an error message.
+	 *
+	 * @since 4.7.0
+	 *
+	 * @param string $color
+	 * @return mixed
+	 */
+	public function _validate_header_video( $validity, $value ) {
+		$video = get_attached_file( absint( $value ) );
+		if ( $video ) {
+			$size = filesize( $video );
+			if ( 8 < $size / pow( 1024, 2 ) ) { // Check whether the size is larger than 8MB.
+				$validity->add( 'size_too_large', __( 'This video file is too large to use as a header video. Try a shorter video or optimize the compression settings and re-upload.' ) );
+			}
+		}
+		return $validity;
+	}
+
+	/**
 	 * Callback for rendering the custom logo, used in the custom_logo partial.
 	 *
 	 * This method exists because the partial object and context data are passed
Index: src/wp-includes/customize/class-wp-customize-header-image-control.php
===================================================================
--- src/wp-includes/customize/class-wp-customize-header-image-control.php	(revision 38842)
+++ src/wp-includes/customize/class-wp-customize-header-image-control.php	(working copy)
@@ -166,9 +166,14 @@
 		$height = absint( get_theme_support( 'custom-header', 'height' ) );
 		?>
 		<div class="customize-control-content">
-			<p class="customizer-section-intro">
+			<?php if ( current_theme_supports( 'custom-header', 'video' ) ) {
+				echo '<span class="customize-control-title">' . $this->label . '</span>';
+			} ?>
+			<p class="customizer-section-intro customize-control-description">
 				<?php
-				if ( $width && $height ) {
+				if ( current_theme_supports( 'custom-header', 'video' ) ) {
+					_e( 'While you can crop images to your liking after clicking <strong>Add new image</strong>, we recommend matching the size of your video.' );
+				} elseif ( $width && $height ) {
 					/* translators: %s: header size in pixels */
 					printf( __( 'While you can crop images to your liking after clicking <strong>Add new image</strong>, your theme recommends a header size of %s pixels.' ),
 						sprintf( '<strong>%s &times; %s</strong>', $width, $height )
Index: src/wp-includes/theme.php
===================================================================
--- src/wp-includes/theme.php	(revision 38842)
+++ src/wp-includes/theme.php	(working copy)
@@ -1264,6 +1264,7 @@
 		'thumbnail_url' => '',
 		'width'         => get_theme_support( 'custom-header', 'width' ),
 		'height'        => get_theme_support( 'custom-header', 'height' ),
+		'video'         => get_theme_support( 'custom-header', 'video' ),
 	);
 	return (object) wp_parse_args( $data, $default );
 }
@@ -1311,6 +1312,152 @@
 }
 
 /**
+ * Check whether a header video is set or not.
+ *
+ * @since 4.7.0
+ *
+ * @see get_header_video_url()
+ *
+ * @return bool Whether a header video is set or not.
+ */
+function has_header_video() {
+	return (bool) get_header_video_url();
+}
+
+/* Retrieve header video URL for custom header.
+*
+* @since 4.7.0
+*
+* @return string|false
+*/
+function get_header_video_url() {
+	$id = absint( get_theme_mod( 'header_video' ) );
+
+	if ( ! $id ) {
+		return false;
+	}
+
+	if ( $id ) {
+		// We have an attachment ID, need the full URL
+		$url = wp_get_attachment_url( $id );
+	}
+
+	return esc_url_raw( set_url_scheme( $url ) );
+}
+
+/**
+ * Create video tag markup for a custom header video.
+ *
+ * @since 4.7.0
+ *
+ * @param array $attr Optional. Additional attributes for the image tag. Can be used
+ *                              to override the default attributes. Default empty.
+ * @return string HTML image element markup or empty string on failure.
+ */
+function get_header_video_tag( $attr = array() ) {
+	$header = get_custom_header();
+	$video = get_header_video_url();
+	$image = get_header_image();
+
+	if ( empty( $video ) ) {
+		if ( is_customize_preview() ) { // If there was a video an it was removed, fall back to an image.
+			if ( ! empty( $image ) ) {
+				return get_header_image_tag();
+			}
+		}
+		return '';
+	}
+
+	$width = absint( $header->width );
+	$height = absint( $header->height );
+
+	$attr = wp_parse_args(
+		$attr,
+		array(
+			'src' => get_header_video_url(),
+			'width' => $width,
+			'height' => $height,
+			'controls' => '',
+			'autoplay' => '',
+			'loop' => '',
+			'muted' => '',
+		)
+	);
+
+	// Use the header image as poster attribute and video fallback.
+	if ( ! empty( $image ) ) {
+		$attr['poster'] = $header->url;
+		$content = get_header_image_tag();
+	} else {
+		$content = __( 'Your browser cannot play this video.' );
+	}
+
+	$attr = array_map( 'esc_attr', $attr );
+	$html = '<video';
+
+	foreach ( $attr as $name => $value ) {
+		$html .= ' ' . $name . '="' . $value . '"';
+	}
+
+	$html .= '>' . $content . '</video>';
+
+	/**
+	 * Filters the markup of header videos.
+	 *
+	 * @since 4.7.0
+	 *
+	 * @param string $html   The HTML image tag markup being filtered.
+	 * @param object $header The custom header object returned by 'get_custom_header()'.
+	 * @param array  $attr   Array of the attributes for the image tag.
+	 */
+	return apply_filters( 'get_header_video_tag', $html, $header, $attr );
+}
+
+/**
+ * Display the video markup for a custom header video.
+ *
+ * @since 4.7.0
+ *
+ * @param array $attr Optional. Attributes for the image markup. Default empty.
+ */
+function the_header_video_tag( $attr = array() ) {
+	echo '<div id="wp-custom-header-video">' . get_header_video_tag( $attr ) . '</div>';
+}
+
+/**
+ * Display header video URL.
+ *
+ * @since 4.7.0
+ */
+function the_header_video_url() {
+	$video = get_header_video_url();
+	if ( $video ) {
+		echo esc_url( $video );
+	}
+}
+
+/**
+ * Display the video and image markup for a custom header.
+ *
+ * @since 4.7.0
+ */
+function the_custom_header() {
+	$video = get_header_video_url();
+	$image = get_header_image();
+
+	if ( ! empty( $video ) ) {
+		the_header_video_tag();
+	} elseif ( is_customize_preview() ) {
+		echo '<div id="wp-custom-header-video"></div>';
+		if ( ! empty( $image ) ) {
+			the_header_image_tag();
+		}
+	} elseif ( ! empty( $image ) ) {
+		the_header_image_tag();
+	}
+}
+
+/**
  * Retrieve background image for custom background.
  *
  * @since 3.0.0
@@ -1706,6 +1853,7 @@
 				'wp-head-callback' => '',
 				'admin-head-callback' => '',
 				'admin-preview-callback' => '',
+				'video' => false,
 			);
 
 			$jit = isset( $args[0]['__jit'] );
