Index: src/wp-includes/js/mce-view.js
===================================================================
--- src/wp-includes/js/mce-view.js	(revision 28181)
+++ src/wp-includes/js/mce-view.js	(working copy)
@@ -451,10 +451,6 @@
 				firefox = this.ua.is( 'ff' ),
 				className = '.wp-' +  this.shortcode.tag + '-shortcode';
 
-			if ( this.player ) {
-				this.unsetPlayer();
-			}
-
 			media = $( node ).find( className );
 
 			if ( ! this.isCompatible( media ) ) {
@@ -495,12 +491,7 @@
 		},
 
 		unbind: function() {
-			var self = this;
-			this.pauseAllPlayers();
-			_.each( this.players, function (player) {
-				self.removePlayer( player );
-			} );
-			this.players = [];
+			this.unsetPlayers();
 		}
 	});
 	_.extend( wp.mce.media.View.prototype, wp.media.mixin );
@@ -547,22 +538,10 @@
 		template:  media.template('editor-playlist'),
 
 		initialize: function( options ) {
+			this.players = [];
 			this.data = {};
 			this.attachments = [];
 			this.shortcode = options.shortcode;
-			_.bindAll( this, 'setPlayer' );
-			$(this).on('ready', this.setNode);
-		},
-
-		/**
-		 * Set the element context for the view, and then fetch the playlist's
-		 *   associated attachments.
-		 *
-		 * @param {Event} e
-		 * @param {HTMLElement} node
-		 */
-		setNode: function(e, node) {
-			this.node = node;
 			this.fetch();
 		},
 
@@ -571,7 +550,7 @@
 		 */
 		fetch: function() {
 			this.attachments = wp.media.playlist.attachments( this.shortcode );
-			this.attachments.more().done( this.setPlayer );
+			this.dfd = this.attachments.more().done( _.bind( this.render, this ) );
 		},
 
 		/**
@@ -582,36 +561,33 @@
 		 * @global WPPlaylistView
 		 * @global tinymce.editors
 		 */
-		setPlayer: function() {
-			var p,
-				html = this.getHtml(),
-				t = this.encodedText,
-				self = this;
+		render: function() {
+			var html = this.getHtml(), self = this;
 
-			this.unsetPlayer();
+			this.unsetPlayers();
 
 			_.each( tinymce.editors, function( editor ) {
 				var doc;
 				if ( editor.plugins.wpview ) {
 					doc = editor.getDoc();
-					$( doc ).find( '[data-wpview-text="' + t + '"]' ).each(function(i, elem) {
+					$( doc ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each(function (i, elem) {
 						var node = $( elem );
-						node.html( html );
-						self.node = elem;
+
+						// The <ins> is used to mark the end of the wrapper div. Needed when comparing
+						// the content as string for preventing extra undo levels.
+						node.html( html ).append( '<ins data-wpview-end="1"></ins>' );
+
+						if ( ! self.data.tracks ) {
+							return;
+						}
+
+						self.players.push( new WPPlaylistView({
+							el: $( elem ).find( '.wp-playlist' ).get(0),
+							metadata: self.data
+						}).player );
 					});
 				}
 			}, this );
-
-			if ( ! this.data.tracks ) {
-				return;
-			}
-
-			p = new WPPlaylistView({
-				el: $( self.node ).find( '.wp-playlist' ).get(0),
-				metadata: this.data
-			});
-
-			this.player = p.player;
 		},
 
 		/**
Index: src/wp-includes/js/media-audiovideo.js
===================================================================
--- src/wp-includes/js/media-audiovideo.js	(revision 28181)
+++ src/wp-includes/js/media-audiovideo.js	(working copy)
@@ -162,11 +162,13 @@
 		 *
 		 *  Examples: modal closes, shortcode properties are removed, etc.
 		 */
-		unsetPlayer : function() {
-			if ( this.player ) {
+		unsetPlayers : function() {
+			if ( this.players && this.players.length ) {
 				wp.media.mixin.pauseAllPlayers();
-				wp.media.mixin.removePlayer( this.player );
-				this.player = false;
+				_.each( this.players, function (player) {
+					wp.media.mixin.removePlayer( player );
+				} );
+				this.players = [];
 			}
 		}
 	};
@@ -705,10 +707,10 @@
 	media.view.MediaDetails = media.view.Settings.AttachmentDisplay.extend({
 		initialize: function() {
 			_.bindAll(this, 'success');
-
-			this.listenTo( this.controller, 'close', media.mixin.unsetPlayer );
+			this.players = [];
+			this.listenTo( this.controller, 'close', media.mixin.unsetPlayers );
 			this.on( 'ready', this.setPlayer );
-			this.on( 'media:setting:remove', media.mixin.unsetPlayer, this );
+			this.on( 'media:setting:remove', media.mixin.unsetPlayers, this );
 			this.on( 'media:setting:remove', this.render );
 			this.on( 'media:setting:remove', this.setPlayer );
 			this.events = _.extend( this.events, {
@@ -764,8 +766,8 @@
 		 * @global MediaElementPlayer
 		 */
 		setPlayer : function() {
-			if ( ! this.player && this.media ) {
-				this.player = new MediaElementPlayer( this.media, this.settings );
+			if ( ! this.players.length && this.media ) {
+				this.players.push( new MediaElementPlayer( this.media, this.settings ) );
 			}
 		},
 
Index: src/wp-includes/media-template.php
===================================================================
--- src/wp-includes/media-template.php	(revision 28181)
+++ src/wp-includes/media-template.php	(working copy)
@@ -16,7 +16,8 @@
 function wp_underscore_audio_template() {
 	$audio_types = wp_get_audio_extensions();
 ?>
-<audio controls
+<audio style="visibility: hidden"
+	controls
 	class="wp-audio-shortcode"
 	width="{{ _.isUndefined( data.model.width ) ? 400 : data.model.width }}"
 	preload="{{ _.isUndefined( data.model.preload ) ? 'none' : data.model.preload }}"
Index: src/wp-includes/media.php
===================================================================
--- src/wp-includes/media.php	(revision 28181)
+++ src/wp-includes/media.php	(working copy)
@@ -1353,6 +1353,8 @@
 		echo (int) $theme_width;
 	?>"<?php if ( 'video' === $safe_type ):
 		echo ' height="', (int) $theme_height, '"';
+	else:
+		echo ' style="visibility: hidden"';
 	endif; ?>></<?php echo $safe_type ?>>
 	<div class="wp-playlist-next"></div>
 	<div class="wp-playlist-prev"></div>
@@ -1555,7 +1557,7 @@
 		'loop'     => $loop,
 		'autoplay' => $autoplay,
 		'preload'  => $preload,
-		'style'    => 'width: 100%',
+		'style'    => 'width: 100%; visibility: hidden;',
 	);
 
 	// These ones should just be omitted altogether if they are blank
