Index: src/wp-includes/theme.php
===================================================================
--- src/wp-includes/theme.php	(revision 39227)
+++ src/wp-includes/theme.php	(working copy)
@@ -1400,7 +1400,7 @@
  * @return bool True if a custom header is set. False if not.
  */
 function has_custom_header() {
-	if ( has_header_image() || ( is_front_page() && has_header_video() ) ) {
+	if ( has_header_image() || ( has_header_video() && is_header_video_active() ) ) {
 		return true;
 	}
 
@@ -1408,6 +1408,36 @@
 }
 
 /**
+ * Checks whether the custom header video is eligible to show on the current page.
+ *
+ * @since 4.7.0
+ *
+ * @return bool True if the custom header video should be shown. False if not.
+ */
+function is_header_video_active() {
+	if ( ! get_theme_support( 'custom-header', 'video' ) ) {
+		return false;
+	}
+
+	$video_active_cb = get_theme_support( 'custom-header', 'video-active-callback' );
+
+	if ( empty( $video_active_cb ) || ! is_callable( $video_active_cb ) ) {
+		$show_video = true;
+	} else {
+		$show_video = call_user_func( $video_active_cb );
+	}
+
+	/**
+	 * Modify whether the custom header video is eligible to show on the current page.
+	 *
+	 * @since 4.7.0
+	 *
+	 * @param bool $show_video Whether the custom header video should be shown. Defaults to the value of the theme setting for the `custom-header`'s `video-active-callback`. If no callback is set, the default value is that of `is_front_page()`.
+	 */
+	return apply_filters( 'is_header_video_active', $show_video );
+}
+
+/**
  * Retrieve the markup for a custom header.
  *
  * The container div will always be returned in the Customizer preview.
@@ -1442,7 +1472,7 @@
 
 	echo $custom_header;
 
-	if ( is_front_page() && ( has_header_video() || is_customize_preview() ) ) {
+	if ( is_header_video_active() && ( has_header_video() || is_customize_preview() ) ) {
 		wp_enqueue_script( 'wp-custom-header' );
 		wp_localize_script( 'wp-custom-header', '_wpCustomHeaderSettings', get_header_video_settings() );
 	}
@@ -2058,6 +2088,7 @@
 				'admin-head-callback' => '',
 				'admin-preview-callback' => '',
 				'video' => false,
+				'video-active-callback' => 'is_front_page',
 			);
 
 			$jit = isset( $args[0]['__jit'] );
@@ -2319,10 +2350,13 @@
 			if ( ! did_action( 'wp_loaded' ) )
 				break;
 			$support = get_theme_support( 'custom-header' );
-			if ( $support[0]['wp-head-callback'] )
+			if ( isset( $support[0]['wp-head-callback'] ) ) {
 				remove_action( 'wp_head', $support[0]['wp-head-callback'] );
-			remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) );
-			unset( $GLOBALS['custom_image_header'] );
+			}
+			if ( isset( $GLOBALS['custom_image_header'] ) ) {
+				remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) );
+				unset( $GLOBALS['custom_image_header'] );
+			}
 			break;
 
 		case 'custom-background' :
Index: tests/phpunit/data/images/video.mp4
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: tests/phpunit/data/images/video.mp4
===================================================================
--- tests/phpunit/data/images/video.mp4	(revision 0)
+++ tests/phpunit/data/images/video.mp4	(working copy)

Property changes on: tests/phpunit/data/images/video.mp4
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: tests/phpunit/tests/theme/customHeader.php
===================================================================
--- tests/phpunit/tests/theme/customHeader.php	(revision 0)
+++ tests/phpunit/tests/theme/customHeader.php	(working copy)
@@ -0,0 +1,344 @@
+<?php
+/**
+ * @group themes
+ */
+class Tests_Theme_Custom_Header extends WP_UnitTestCase {
+	protected static $header_video_id;
+
+	public static function wpSetUpBeforeClass( $factory ) {
+		$file = DIR_TESTDATA . '/images/video.mp4';
+		self::$header_video_id = $factory->attachment->create_upload_object( $file );
+	}
+
+	function setUp() {
+		parent::setUp();
+
+		require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' );
+		$GLOBALS['wp_customize'] = new WP_Customize_Manager();
+		$this->customize_manager = $GLOBALS['wp_customize'];
+
+		wp_dequeue_script( 'wp-custom-header' );
+	}
+
+	function tearDown() {
+		$this->customize_manager = null;
+		unset( $GLOBALS['wp_customize'] );
+
+		remove_theme_support( 'custom-header' );
+		remove_theme_mod( 'header_image' );
+		remove_theme_mod( 'header_image_data' );
+		remove_theme_mod( 'header_video' );
+		remove_theme_mod( 'external_header_video' );
+
+		parent::tearDown();
+	}
+
+	function test_add_and_remove_theme_support() {
+		$this->_add_theme_support();
+		$this->assertTrue( current_theme_supports( 'custom-header' ) );
+		remove_theme_support( 'custom-header' );
+		$this->assertFalse( current_theme_supports( 'custom-header' ) );
+	}
+
+	function test_get_header_image_without_registered_default() {
+		$this->_add_theme_support();
+		$image = get_header_image();
+		$this->assertFalse( has_header_image() );
+		$this->assertEmpty( $image );
+	}
+
+	function test_get_header_image_with_registered_default() {
+		$default = 'http://localhost/default-header.jpg';
+		$this->_add_theme_support( array( 'default-image' => $default ) );
+
+		$image = get_header_image();
+		$this->assertTrue( has_header_image() );
+		$this->assertEquals( $default, $image );
+	}
+
+	function test_get_header_image_from_theme_mod() {
+		$default = 'http://localhost/default-header.jpg';
+		$custom = 'http://localhost/custom-header.jpg';
+		$this->_add_theme_support( array( 'default-image' => $default ) );
+
+		set_theme_mod( 'header_image', $custom );
+		$image = get_header_image();
+		$this->assertEquals( $custom, $image );
+		$this->assertTrue( has_header_image() );
+
+		set_theme_mod( 'header_image', 'remove-header' );
+		$image = get_header_image();
+		$this->assertFalse( has_header_image() );
+		$this->assertFalse( $image );
+	}
+
+	function test_get_header_image_tag_without_registered_default_image() {
+		$this->_add_theme_support();
+		$html = get_header_image_tag();
+		$this->assertEmpty( $html );
+	}
+
+	function test_get_header_image_tag_with_registered_default_image() {
+		$default = 'http://localhost/default-header.jpg';
+		$this->_add_theme_support( array( 'default-image' => $default ) );
+
+		$html = get_header_image_tag();
+		$this->assertStringStartsWith( '<img ', $html );
+		$this->assertContains( sprintf( 'src="%s"', $default ), $html );
+	}
+
+	/**
+	 * @ticket 38633
+	 */
+	function test_get_header_image_tag_with_registered_default_image_and_remove_header_theme_mod() {
+		$default = 'http://localhost/default-header.jpg';
+		$this->_add_theme_support( array( 'default-image' => $default ) );
+
+		set_theme_mod( 'header_image', 'remove-header' );
+		$html = get_header_image_tag();
+		$this->assertEmpty( $html );
+	}
+
+	function test_get_header_image_tag_with_registered_default_image_and_custom_theme_mod() {
+		$default = 'http://localhost/default-header.jpg';
+		$custom = 'http://localhost/custom-header.jpg';
+		$this->_add_theme_support( array( 'default-image' => $default ) );
+
+		set_theme_mod( 'header_image', $custom );
+		$html = get_header_image_tag();
+		$this->assertStringStartsWith( '<img ', $html );
+		$this->assertContains( sprintf( 'src="%s"', $custom ), $html );
+	}
+
+	function test_get_custom_header_markup_without_registered_default_image() {
+		$this->_add_theme_support();
+
+		$html = get_custom_header_markup();
+		$this->assertFalse( has_custom_header() );
+		$this->assertEmpty( $html );
+
+		// The container should always be returned in the Customizer preview.
+		$this->_set_customize_previewing( true );
+		$html = get_custom_header_markup();
+		$this->assertEquals( '<div id="wp-custom-header" class="wp-custom-header"></div>', $html );
+	}
+
+	function test_get_custom_header_markup_with_registered_default_image() {
+		$default = 'http://localhost/default-header.jpg';
+		$this->_add_theme_support( array( 'default-image' => $default ) );
+		$html = get_custom_header_markup();
+		$this->assertTrue( has_custom_header() );
+		$this->assertStringStartsWith( '<div id="wp-custom-header" class="wp-custom-header">', $html );
+		$this->assertContains( sprintf( 'src="%s"', $default ), $html );
+	}
+
+	function test_get_header_video_url() {
+		$this->_add_theme_support( array( 'video' => true ) );
+
+		$this->assertFalse( has_header_video() );
+		set_theme_mod( 'header_video', self::$header_video_id );
+		$this->assertTrue( has_header_video() );
+		$this->assertEquals( wp_get_attachment_url( self::$header_video_id ), get_header_video_url() );
+	}
+
+	function test_get_external_header_video_url() {
+		$external = 'http://example.com/custom-video.mp4';
+		$this->_add_theme_support( array( 'video' => true ) );
+
+		$this->assertFalse( has_header_video() );
+		set_theme_mod( 'external_header_video', $external );
+		$this->assertTrue( has_header_video() );
+		$this->assertEquals( $external, get_header_video_url() );
+	}
+
+	function test_get_header_video_url_prefers_local_video() {
+		$external = 'http://example.com/custom-video.mp4';
+		$this->_add_theme_support( array( 'video' => true ) );
+
+		set_theme_mod( 'header_video', self::$header_video_id );
+		set_theme_mod( 'external_header_video', $external );
+		$this->assertEquals( wp_get_attachment_url( self::$header_video_id ), get_header_video_url() );
+	}
+
+	function test_get_custom_header_markup_with_video_and_without_an_image() {
+		$custom = 'http://localhost/custom-video.mp4';
+		$this->_add_theme_support( array( 'video' => true, 'video-active-callback' => '__return_true' ) );
+
+		set_theme_mod( 'external_header_video', $custom );
+		$html = get_custom_header_markup();
+		$this->assertTrue( has_header_video() );
+		$this->assertTrue( has_custom_header() );
+		$this->assertEquals( '<div id="wp-custom-header" class="wp-custom-header"></div>', $html );
+	}
+
+	function test_header_script_is_not_enqueued_by_the_custom_header_markup_without_video() {
+		$this->_add_theme_support( array( 'video' => true, 'video-active-callback' => '__return_true' ) );
+
+		ob_start();
+		the_custom_header_markup();
+		ob_end_clean();
+		$this->assertFalse( wp_script_is( 'wp-custom-header', 'enqueued' ) );
+
+		set_theme_mod( 'header_image', 'http://localhost/custom-header.jpg' );
+
+		ob_start();
+		the_custom_header_markup();
+		ob_end_clean();
+		$this->assertFalse( wp_script_is( 'wp-custom-header', 'enqueued' ) );
+	}
+
+	function test_header_script_is_not_enqueued_by_the_custom_header_markup_when_active_callback_is_false() {
+		$this->_add_theme_support( array( 'video' => true, 'video-active-callback' => '__return_false' ) );
+		set_theme_mod( 'external_header_video', 'http://localhost/custom-video.mp4' );
+
+		ob_start();
+		the_custom_header_markup();
+		ob_end_clean();
+		$this->assertFalse( wp_script_is( 'wp-custom-header', 'enqueued' ) );
+	}
+
+	function test_header_script_is_enqueued_by_the_custom_header_markup_without_video_when_previewing_in_customizer() {
+		$this->_add_theme_support( array( 'video' => true, 'video-active-callback' => '__return_true' ) );
+		$this->_set_customize_previewing( true );
+
+		ob_start();
+		the_custom_header_markup();
+		ob_end_clean();
+		$this->assertTrue( wp_script_is( 'wp-custom-header', 'enqueued' ) );
+	}
+
+	function test_header_script_is_enqueued_by_the_custom_header_markup_with_video() {
+		$this->_add_theme_support( array( 'video' => true, 'video-active-callback' => '__return_true' ) );
+		set_theme_mod( 'external_header_video', 'http://localhost/custom-video.mp4' );
+
+		ob_start();
+		the_custom_header_markup();
+		ob_end_clean();
+		$this->assertTrue( wp_script_is( 'wp-custom-header', 'enqueued' ) );
+	}
+
+	/**
+	 * @ticket 38738
+	 */
+	function test_video_header_callback_front_page_from_front_page() {
+		$this->_add_theme_support( array(
+			'video' => true,
+		) );
+
+		$this->go_to( home_url() );
+
+		$result = is_header_video_active();
+
+		$this->assertTrue( $result );
+	}
+
+	/**
+	 * @ticket 38738
+	 */
+	function test_video_header_callback_front_page_from_elsewhere() {
+		$this->_add_theme_support( array(
+			'video' => true,
+		) );
+
+		$p = self::factory()->post->create( array(
+			'post_status' => 'publish',
+		) );
+
+		$this->go_to( get_permalink( $p ) );
+
+		$result = is_header_video_active();
+
+		$this->assertFalse( $result );
+	}
+
+	/**
+	 * @ticket 38738
+	 */
+	function test_video_header_callback_globally_from_front_page() {
+		$this->_add_theme_support( array(
+			'video' => true,
+			'video-active-callback' => '__return_true',
+		) );
+
+		$this->go_to( home_url() );
+
+		$result = is_header_video_active();
+
+		$this->assertTrue( $result );
+	}
+
+	/**
+	 * @ticket 38738
+	 */
+	function test_video_header_callback_globally_from_elsewhere() {
+		$this->_add_theme_support( array(
+			'video' => true,
+			'video-active-callback' => '__return_true',
+		) );
+
+		$p = self::factory()->post->create( array(
+			'post_status' => 'publish',
+		) );
+
+		$this->go_to( get_permalink( $p ) );
+
+		$result = is_header_video_active();
+
+		$this->assertTrue( $result );
+	}
+
+	/**
+	 * @ticket 38738
+	 */
+	function test_video_header_callback_globally_with_negative_filter() {
+		$this->_add_theme_support( array(
+			'video' => true,
+			'video-active-callback' => '__return_true',
+		) );
+
+		$p = self::factory()->post->create( array(
+			'post_status' => 'publish',
+		) );
+
+		$this->go_to( get_permalink( $p ) );
+
+		add_filter( 'is_header_video_active', '__return_false' );
+		$result = is_header_video_active();
+		remove_filter( 'is_header_video_active', '__return_false' );
+
+		$this->assertFalse( $result );
+	}
+
+	/**
+	 * Adds arguments directly to the $_wp_theme_features global. Calling
+	 * add_theme_support( 'custom-header' ) will poison subsequent tests since
+	 * it defines constants.
+	 */
+	function _add_theme_support( $args = array() ) {
+		global $_wp_theme_features;
+
+		$_wp_theme_features['custom-header'][0] = wp_parse_args( $args, array(
+			'default-image' => '',
+			'random-default' => false,
+			'width' => 0,
+			'height' => 0,
+			'flex-height' => false,
+			'flex-width' => false,
+			'default-text-color' => '',
+			'header-text' => true,
+			'uploads' => true,
+			'wp-head-callback' => '',
+			'admin-head-callback' => '',
+			'admin-preview-callback' => '',
+			'video' => false,
+			'video-active-callback' => 'is_front_page',
+		) );
+	}
+
+	function _set_customize_previewing( $value ) {
+		$class = new ReflectionClass( 'WP_Customize_Manager' );
+		$property = $class->getProperty( 'previewing' );
+		$property->setAccessible( true );
+		$property->setValue( $this->customize_manager, $value );
+	}
+}
