diff --git src/bp-activity/bp-activity-actions.php src/bp-activity/bp-activity-actions.php
index f648b41..18ee4c1 100644
--- src/bp-activity/bp-activity-actions.php
+++ src/bp-activity/bp-activity-actions.php
@@ -744,6 +744,18 @@ function bp_activity_setup_akismet() {
 	$bp->activity->akismet = new BP_Akismet();
 }
 
+
+/**
+ * Loads our activity oEmbed component.
+ *
+ * @since 2.5.0
+ */
+function bp_activity_setup_oembed() {
+	if ( bp_get_major_wp_version() >= 4.4 && bp_is_active( 'activity', 'embeds' ) ) {
+		buddypress()->activity->oembed = new BP_Activity_oEmbed_Component;
+	}
+}
+
 /**
  * AJAX endpoint for Suggestions API lookups.
  *
diff --git src/bp-activity/bp-activity-loader.php src/bp-activity/bp-activity-loader.php
index dfdf325..ff8dd0f 100644
--- src/bp-activity/bp-activity-loader.php
+++ src/bp-activity/bp-activity-loader.php
@@ -31,6 +31,7 @@ class BP_Activity_Component extends BP_Component {
 			array(
 				'adminbar_myaccount_order' => 10,
 				'search_query_arg' => 'activity_search',
+				'features' => array( 'embeds' )
 			)
 		);
 	}
@@ -67,6 +68,11 @@ class BP_Activity_Component extends BP_Component {
 			$includes[] = 'akismet';
 		}
 
+		// Embeds - only applicable for WP 4.4+
+		if ( bp_get_major_wp_version() >= 4.4 && bp_is_active( $this->id, 'embeds' ) ) {
+			$includes[] = 'classes/class-bp-activity-oembed-component';
+		}
+
 		if ( is_admin() ) {
 			$includes[] = 'admin';
 		}
@@ -365,6 +371,9 @@ class BP_Activity_Component extends BP_Component {
 		// Spam prevention.
 		add_action( 'bp_include', 'bp_activity_setup_akismet' );
 
+		// oEmbed handler.
+		add_action( 'bp_loaded', 'bp_activity_setup_oembed' );
+
 		parent::setup_actions();
 	}
 
diff --git src/bp-activity/classes/class-bp-activity-oembed-component.php src/bp-activity/classes/class-bp-activity-oembed-component.php
new file mode 100644
index 0000000..95d174d
--- /dev/null
+++ src/bp-activity/classes/class-bp-activity-oembed-component.php
@@ -0,0 +1,203 @@
+<?php
+/**
+ * BuddyPress Activity Classes.
+ *
+ * @package BuddyPress
+ * @subpackage Embeds
+ */
+
+// Exit if accessed directly.
+defined( 'ABSPATH' ) || exit;
+
+require_once( buddypress()->plugin_dir . '/bp-core/classes/class-bp-oembed-component.php' );
+
+/**
+ * oEmbed handler to respond and render single activity items.
+ *
+ * @since 2.5.0
+ */
+class BP_Activity_oEmbed_Component extends BP_oEmbed_Component {
+	/**
+	 * Custom oEmbed slug endpoint.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @var string
+	 */
+	public $slug_endpoint = 'activity';
+
+	/**
+	 * Conditional function to determine if we're on our activity page.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @var string
+	 */
+	protected $page_conditional_fn = 'bp_is_single_activity';
+
+	/**
+	 * Custom hooks.
+	 *
+	 * @since 2.5.0
+	 */
+	protected function custom_hooks() {
+		add_action( 'embed_content_meta', array( $this, 'embed_comments_button' ), 5 );
+	}
+
+	/**
+	 * Output our custom embed template part.
+	 *
+	 * @since 2.5.0
+	 */
+	protected function content() {
+		bp_get_template_part( 'embeds/activity' );
+	}
+
+	/**
+	 * Validates the URL to determine if the activity item is valid.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  string   $url The URL to check.
+	 * @return int|bool Activity ID on success; boolean false on failure.
+	 */
+	protected function validate_url_to_item_id( $url ) {
+		if ( bp_core_enable_root_profiles() ) {
+			$domain = bp_get_root_domain();
+		} else {
+			$domain = bp_get_members_directory_permalink();
+		}
+
+		// Check the URL to see if this is a single activity URL.
+		if ( 0 !== strpos( $url, $domain ) ) {
+			return false;
+		}
+
+		// Check for activity slug.
+		if ( false === strpos( $url, '/' . bp_get_activity_slug() . '/' ) ) {
+			return false;
+		}
+
+		// Do more checks.
+		$url = trim( untrailingslashit( $url ) );
+
+		// Grab the activity ID.
+		$activity_id = (int) substr(
+			$url,
+			strrpos( $url, '/' ) + 1
+		);
+
+		if ( ! empty( $activity_id ) ) {
+			// Check if activity item still exists.
+			$activity = new BP_Activity_Activity( $activity_id );
+
+			// Okay, we're good to go!
+			if ( ! empty( $activity->component ) && 0 === (int) $activity->is_spam ) {
+				return $activity_id;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Sets the oEmbed response data for our activity item.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  int $item_id The activity ID.
+	 * @return array
+	 */
+	protected function set_oembed_response_data( $item_id ) {
+		$activity = new BP_Activity_Activity( $item_id );
+
+		return array(
+			'user_id'    => $activity->user_id,
+			'content'    => $activity->content,
+			'title'      => __( 'Activity', 'buddypress' ),
+			'author_url' => bp_core_get_user_domain( $activity->user_id )
+		);
+	}
+
+	/**
+	 * Sets a custom <blockquote> for our oEmbed fallback HTML.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  int $item_id The activity ID.
+	 * @return string
+	 */
+	protected function set_fallback_html( $item_id ) {
+		$activity    = new BP_Activity_Activity( $item_id );
+		$mentionname = bp_activity_do_mentions() ? ' (@' . bp_activity_get_user_mentionname( $activity->user_id ) . ')' : '';
+		$date        = date_i18n( get_option( 'date_format' ), strtotime( $activity->date_recorded ) );
+
+		// 'wp-embedded-content' CSS class is necessary due to how the embed JS works.
+		$blockquote = sprintf( '<blockquote class="wp-embedded-content bp-activity-item">%1$s%2$s %3$s</blockquote>',
+			apply_filters( 'bp_get_activity_content_body', $activity->content ),
+			'- ' . bp_core_get_user_displayname( $activity->user_id ) . $mentionname,
+			'<a href="' . esc_url( bp_activity_get_permalink( $item_id ) ) . '">' . $date . '</a>'
+		);
+
+		/**
+		 * Filters the fallback HTML used when embedding a BP activity item.
+		 *
+		 * @since 2.5.0
+		 *
+		 * @param string               $blockquote Current fallback HTML
+		 * @param BP_Activity_Activity $activity   Activity object
+		 */
+		return apply_filters( 'bp_activity_embed_fallback_html', $blockquote, $activity );
+	}
+
+	/**
+	 * Sets a custom <iframe> title for our oEmbed item.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  int $item_id The activity ID
+	 * @return string
+	 */
+	protected function set_iframe_title( $item_id ) {
+		return __( 'Embedded Activity Item', 'buddypress' );
+	}
+
+	/**
+	 * Prints the markup for the activity embed comments button.
+	 *
+	 * @since 2.5.0
+	 */
+	public function embed_comments_button() {
+		if ( ! bp_is_single_activity() ) {
+			return;
+		}
+
+		// Make sure our custom permalink shows up in the 'WordPress Embed' block.
+		add_filter( 'the_permalink', array( $this, 'filter_embed_url' ) );
+
+		// Only show comment bubble if we have some activity comments.
+		$count = bp_activity_get_comment_count();
+		if ( empty( $count ) ) {
+			return;
+		}
+	?>
+
+		<div class="wp-embed-comments">
+			<a href="<?php bp_activity_thread_permalink(); ?>">
+				<span class="dashicons dashicons-admin-comments"></span>
+				<?php
+				printf(
+					_n(
+						'%s <span class="screen-reader-text">Comment</span>',
+						'%s <span class="screen-reader-text">Comments</span>',
+						$count
+					),
+					number_format_i18n( $count )
+				);
+				?>
+			</a>
+		</div>
+
+	<?php
+	}
+}
diff --git src/bp-core/bp-core-functions.php src/bp-core/bp-core-functions.php
index f865d7c..8bb63e5 100644
--- src/bp-core/bp-core-functions.php
+++ src/bp-core/bp-core-functions.php
@@ -1666,6 +1666,44 @@ function bp_use_embed_in_private_messages() {
 	return apply_filters( 'bp_use_embed_in_private_messages', !defined( 'BP_EMBED_DISABLE_PRIVATE_MESSAGES' ) || !BP_EMBED_DISABLE_PRIVATE_MESSAGES );
 }
 
+/**
+ * Checks a given content to see if a certain media type exists.
+ *
+ * @since 2.5.0
+ *
+ * @param  string $content The content to check.
+ * @param  string $type    The type to check. See the class constants in the BP_Media_Extractor class.
+ * @return bool
+ */
+function bp_core_check_content_for_media_type( $content = '', $type = 'all' ) {
+	$retval = false;
+
+	$class = new ReflectionClass( 'BP_Media_Extractor' );
+	$bitmask = $class->getConstant( strtoupper( $type ) );
+
+	// Type isn't valid, so bail.
+	if ( empty( $bitmask ) ) {
+		return false;
+	}
+
+	$x = new BP_Media_Extractor;
+	$media = $x->extract( $content, $bitmask );
+
+	if ( 'all' === $type ) {
+		unset( $media['has'] );
+		$media = array_filter( $media );
+		if ( ! empty( $media ) ) {
+			$retval = true;
+		}
+	}
+
+	if ( ! empty( $media[ $type ] ) ) {
+		$retval = true;
+	}
+
+	return $retval;
+}
+
 /** Admin *********************************************************************/
 
 /**
diff --git src/bp-core/bp-core-theme-compatibility.php src/bp-core/bp-core-theme-compatibility.php
index da54dce..0de2895 100644
--- src/bp-core/bp-core-theme-compatibility.php
+++ src/bp-core/bp-core-theme-compatibility.php
@@ -650,6 +650,55 @@ function bp_register_theme_package( $theme = array(), $override = true ) {
 }
 
 /**
+ * Create a dummy WP_Post object.
+ *
+ * @since 2.5.0
+ *
+ * @param  array $args Array of optional arguments. Arguments parallel the properties
+ *                    of {@link WP_Post}; see that class for more details.
+ * @return WP_Post
+ */
+function bp_theme_compat_create_dummy_post( $args = array() ) {
+	$args = wp_parse_args( $args, array(
+		'ID'                    => -9999,
+		'post_status'           => 'public',
+		'post_author'           => 0,
+		'post_parent'           => 0,
+		'post_type'             => 'page',
+		'post_date'             => 0,
+		'post_date_gmt'         => 0,
+		'post_modified'         => 0,
+		'post_modified_gmt'     => 0,
+		'post_content'          => '',
+		'post_title'            => '',
+		'post_excerpt'          => '',
+		'post_content_filtered' => '',
+		'post_mime_type'        => '',
+		'post_password'         => '',
+		'post_name'             => '',
+		'guid'                  => '',
+		'menu_order'            => 0,
+		'pinged'                => '',
+		'to_ping'               => '',
+		'ping_status'           => '',
+		'comment_status'        => 'closed',
+		'comment_count'         => 0,
+		'filter'                => 'raw',
+
+		'is_404'                => false,
+		'is_page'               => false,
+		'is_single'             => false,
+		'is_archive'            => false,
+		'is_tax'                => false,
+	) );
+
+	// Create the dummy post.
+	$post = new WP_Post( (object) $args );
+
+	return $post;
+}
+
+/**
  * Populate various WordPress globals with dummy data to prevent errors.
  *
  * This dummy data is necessary because theme compatibility essentially fakes
@@ -670,7 +719,7 @@ function bp_theme_compat_reset_post( $args = array() ) {
 
 	// Switch defaults if post is set.
 	if ( isset( $wp_query->post ) ) {
-		$dummy = wp_parse_args( $args, array(
+		$args = wp_parse_args( $args, array(
 			'ID'                    => $wp_query->post->ID,
 			'post_status'           => $wp_query->post->post_status,
 			'post_author'           => $wp_query->post->post_author,
@@ -695,55 +744,16 @@ function bp_theme_compat_reset_post( $args = array() ) {
 			'comment_status'        => $wp_query->post->comment_status,
 			'comment_count'         => $wp_query->post->comment_count,
 			'filter'                => $wp_query->post->filter,
-
-			'is_404'                => false,
-			'is_page'               => false,
-			'is_single'             => false,
-			'is_archive'            => false,
-			'is_tax'                => false,
-		) );
-	} else {
-		$dummy = wp_parse_args( $args, array(
-			'ID'                    => -9999,
-			'post_status'           => 'public',
-			'post_author'           => 0,
-			'post_parent'           => 0,
-			'post_type'             => 'page',
-			'post_date'             => 0,
-			'post_date_gmt'         => 0,
-			'post_modified'         => 0,
-			'post_modified_gmt'     => 0,
-			'post_content'          => '',
-			'post_title'            => '',
-			'post_excerpt'          => '',
-			'post_content_filtered' => '',
-			'post_mime_type'        => '',
-			'post_password'         => '',
-			'post_name'             => '',
-			'guid'                  => '',
-			'menu_order'            => 0,
-			'pinged'                => '',
-			'to_ping'               => '',
-			'ping_status'           => '',
-			'comment_status'        => 'closed',
-			'comment_count'         => 0,
-			'filter'                => 'raw',
-
-			'is_404'                => false,
-			'is_page'               => false,
-			'is_single'             => false,
-			'is_archive'            => false,
-			'is_tax'                => false,
 		) );
 	}
 
 	// Bail if dummy post is empty.
-	if ( empty( $dummy ) ) {
+	if ( empty( $args ) ) {
 		return;
 	}
 
 	// Set the $post global.
-	$post = new WP_Post( (object) $dummy );
+	$post = bp_theme_compat_create_dummy_post( $args );
 
 	// Copy the new post global into the main $wp_query.
 	$wp_query->post       = $post;
@@ -751,14 +761,11 @@ function bp_theme_compat_reset_post( $args = array() ) {
 
 	// Prevent comments form from appearing.
 	$wp_query->post_count = 1;
-	$wp_query->is_404     = $dummy['is_404'];
-	$wp_query->is_page    = $dummy['is_page'];
-	$wp_query->is_single  = $dummy['is_single'];
-	$wp_query->is_archive = $dummy['is_archive'];
-	$wp_query->is_tax     = $dummy['is_tax'];
-
-	// Clean up the dummy post.
-	unset( $dummy );
+	$wp_query->is_404     = $post->is_404;
+	$wp_query->is_page    = $post->is_page;
+	$wp_query->is_single  = $post->is_single;
+	$wp_query->is_archive = $post->is_archive;
+	$wp_query->is_tax     = $post->is_tax;
 
 	/**
 	 * Force the header back to 200 status if not a deliberate 404
diff --git src/bp-core/classes/class-bp-oembed-component.php src/bp-core/classes/class-bp-oembed-component.php
new file mode 100644
index 0000000..52dd9c5
--- /dev/null
+++ src/bp-core/classes/class-bp-oembed-component.php
@@ -0,0 +1,404 @@
+<?php
+/**
+ * Core component classes.
+ *
+ * @package BuddyPress
+ * @subpackage Core
+ */
+
+// Exit if accessed directly.
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * API for responding and returning a custom oEmbed request.
+ *
+ * @since 2.5.0
+ */
+abstract class BP_oEmbed_Component {
+
+	/** START PROPERTIES ****************************************************/
+
+	/**
+	 * (required) The slug endpoint.
+	 *
+	 * Should be your component id.
+	 *
+	 * @var string
+	 */
+	public $slug_endpoint = '';
+
+	/**
+	 * (required) Callback to determine if you're on the page requiring oEmbed.
+	 *
+	 * eg. bp_is_single_activity() without the brackets - 'bp_is_single_activity'.
+	 *
+	 * @var string
+	 */
+	protected $page_conditional_fn = '';
+
+	/**
+	 * (optional) Embed template hierarchy.
+	 *
+	 * Define the template hierarchy for your oEmbed rendering.
+	 *
+	 * @var array
+	 */
+	protected $template_hierarchy = array();
+
+	/** END PROPERTIES ******************************************************/
+
+	/**
+	 * Constructor.
+	 */
+	final public function __construct() {
+		$this->setup_properties();
+
+		// Some rudimentary logic checking.
+		if ( empty( $this->slug_endpoint ) ) {
+			$class = get_class( $this );
+			throw new \LogicException( $class . ' class must define $slug_endpoint property' );
+		} elseif ( empty( $this->page_conditional_fn ) || false === is_callable( $this->page_conditional_fn ) ) {
+			$child = get_class( $this );
+			throw new \LogicException( $class . ' class must define $page_conditional_fn property or must be callable' );
+		}
+
+		$this->setup_hooks();
+		$this->custom_hooks();
+	}
+
+	/** REQUIRED METHODS ****************************************************/
+
+	/**
+	 * Add content for your oEmbed response here.
+	 *
+	 * @since 2.5.0
+	 */
+	abstract protected function content();
+
+	/**
+	 * Validate the URL to see if it matches your item ID.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @return int Your item ID
+	 */
+	abstract protected function validate_url_to_item_id( $url );
+
+	/**
+	 * Set the oEmbed response data.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  int   $item_id Your item ID to do checks against.
+	 * @return array Should contain 'user_id', 'content', 'title', 'author_url' as array keys.
+	 *               'author_url' is optional; the rest are required.
+	 */
+	abstract protected function set_oembed_response_data( $item_id );
+
+	/**
+	 * Sets the fallback HTML for the oEmbed response.
+	 *
+	 * In a WordPress oEmbed item, the fallback HTML is a <blockquote>.  This is
+	 * usually hidden after the <iframe> is loaded.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  int    $item_id Your item ID to do checks against.
+	 * @return string Fallback HTML you want to output.
+	 */
+	abstract protected function set_fallback_html( $item_id );
+
+	/** OPTIONAL METHODS ****************************************************/
+
+	/**
+	 * Set permalink for oEmbed link discovery.
+	 *
+	 * This method will be called on the page we want to oEmbed.  Override in your
+	 * extended class method if needed.
+	 *
+	 * @since 2.5.0
+	 */
+	protected function set_permalink() {
+		return bp_get_requested_url();
+	}
+
+	/**
+	 * Set the iframe title.
+	 *
+	 * If not set, this will fallback to WP's 'Embedded WordPress Post'.
+	 *
+	 * @param int $item_id The item ID to do checks for.
+	 * @return string
+	 */
+	protected function set_iframe_title( $item_id ) {}
+
+	/**
+	 * Do what you need to do here to initialize any custom hooks.
+	 *
+	 * @since 2.5.0
+	 */
+	protected function custom_hooks() {}
+
+	/** SET UP **************************************************************/
+
+	/**
+	 * Set up properties.
+	 *
+	 * @since 2.5.0
+	 */
+	protected function setup_properties() {
+		$this->slug_endpoint = sanitize_title( $this->slug_endpoint );
+	}
+
+	/**
+	 * Hooks! We do the dirty work here, so you don't have to! :)
+	 *
+	 * @since 2.5.0
+	 */
+	protected function setup_hooks() {
+		add_action( 'rest_api_init',  array( $this, 'register_route' ) );
+		add_action( 'embed_content',  array( $this, 'inject_content' ) );
+
+		add_filter( 'embed_template', array( $this, 'filter_template' ) );
+		add_filter( 'post_embed_url', array( $this, 'filter_embed_url' ) );
+		add_filter( 'embed_html',     array( $this, 'filter_embed_html' ) );
+		add_filter( 'oembed_discovery_links', array( $this, 'add_oembed_discovery_links' ) );
+	}
+
+	/** HOOKS ***************************************************************/
+
+	/**
+	 * Register the oEmbed REST API route.
+	 *
+	 * @since 2.5.0
+	 */
+	public function register_route() {
+		/** This filter is documented in wp-includes/class-wp-oembed-controller.php */
+		$maxwidth = apply_filters( 'oembed_default_width', 600 );
+
+		register_rest_route( 'oembed/1.0', "/embed/{$this->slug_endpoint}", array(
+			array(
+				'methods'  => WP_REST_Server::READABLE,
+				'callback' => array( $this, 'get_item' ),
+				'args'     => array(
+					'url'      => array(
+						'required'          => true,
+						'sanitize_callback' => 'esc_url_raw',
+					),
+					'format'   => array(
+						'default'           => 'json',
+						'sanitize_callback' => 'wp_oembed_ensure_format',
+					),
+					'maxwidth' => array(
+						'default'           => $maxwidth,
+						'sanitize_callback' => 'absint',
+					),
+				),
+			),
+		) );
+	}
+
+	/**
+	 * Filters the embed template.
+	 *
+	 * Override if needed.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  string $template File path to current embed template.
+	 * @return string
+	 */
+	public function filter_template( $template ) {
+		$conditional = $this->page_conditional_fn;
+
+		if ( ! $conditional() || is_404() ) {
+			return $template;
+		}
+
+		// Embed template hierarchy!
+		return bp_locate_template( array(
+			'embeds/template.php'
+		) );
+	}
+
+	/**
+	 * Adds oEmbed discovery links on single activity pages.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  string $retval Current discovery links.
+	 * @return string
+	 */
+	public function add_oembed_discovery_links( $retval ) {
+		$conditional = $this->page_conditional_fn;
+		if ( ! $conditional() ) {
+			return $retval;
+		}
+
+		$permalink = $this->set_permalink();
+		if ( empty( $permalink ) ) {
+			return $retval;
+		}
+
+		add_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
+
+		$retval = '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink ) ) . '" />' . "\n";
+
+		if ( class_exists( 'SimpleXMLElement' ) ) {
+			$retval .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink, 'xml' ) ) . '" />' . "\n";
+		}
+
+		remove_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
+
+		return $retval;
+	}
+
+	/**
+	 * Callback for the API endpoint.
+	 *
+	 * Returns the JSON object for the item.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  WP_REST_Request $request Full data about the request.
+	 * @return WP_Error|array oEmbed response data or WP_Error on failure.
+	 */
+	public function get_item( $request ) {
+		$url = $request['url'];
+
+		$data = false;
+
+		$item_id = (int) $this->validate_url_to_item_id( $url );
+
+		if ( ! empty( $item_id ) ) {
+			$item = $this->set_oembed_response_data( $item_id );
+
+			// Create dummy post to piggyback off of get_oembed_response_data()
+			$post = bp_theme_compat_create_dummy_post( array(
+				'post_author'  => $item['user_id'],
+				'post_title'   => $item['title'],
+				'post_content' => $item['content'],
+
+				// This passes the get_oembed_response_data() check.
+				'post_status'  => 'publish'
+			) );
+
+			// Add markers to tell that we're embedding a single activity.
+			// This is needed for various oEmbed response data filtering.
+			if ( empty( buddypress()->{$this->slug_endpoint} ) ) {
+				buddypress()->$this->slug_endpoint = new stdClass;
+			}
+			buddypress()->{$this->slug_endpoint}->embedurl_in_progress = $url;
+			buddypress()->{$this->slug_endpoint}->embedid_in_progress  = $item_id;
+
+			// Use WP's oEmbed response data function.
+			$data = get_oembed_response_data( $post, $request['maxwidth'] );
+
+			// Set custom 'author_url' if we have one.
+			if ( ! empty( $item['author_url'] ) ) {
+				$data['author_url'] = $item['author_url'];
+			}
+		}
+
+		if ( ! $data ) {
+			return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) );
+		}
+
+		return $data;
+	}
+
+	/**
+	 * Pass our BuddyPress activity permalink for embedding.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @see bp_activity_embed_rest_route_callback()
+	 *
+	 * @param  string $retval Current embed URL
+	 * @return string
+	 */
+	public function filter_embed_url( $retval ) {
+		$conditional = $this->page_conditional_fn;
+		if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $conditional() ) {
+			return $retval;
+		}
+
+		$url = $conditional() ? $this->set_permalink() : buddypress()->{$this->slug_endpoint}->embedurl_in_progress;
+		$url = trailingslashit( $url );
+
+		// This is for the 'WordPress Embed' block
+		// @see bp_activity_embed_comments_button()
+		if ( 'the_permalink' !== current_filter() ) {
+			$url = add_query_arg( 'embed', 'true', trailingslashit( $url ) );
+		}
+
+		return $url;
+	}
+
+	/**
+	 * Filters the embed HTML for the default oEmbed fallback HTML.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param  string $retval Current embed HTML
+	 * @return string
+	 */
+	public function filter_embed_html( $retval ) {
+		$conditional = $this->page_conditional_fn;
+		if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $conditional() ) {
+			return $retval;
+		}
+
+		// Remove querystring from URL
+		$url = substr( bp_get_requested_url(), 0, strpos( bp_get_requested_url(), '?' ) );
+
+		$item_id = $conditional() ? $this->validate_url_to_item_id( $url ) : buddypress()->{$this->slug_endpoint}->embedid_in_progress;
+
+		// Change 'Embedded WordPress Post' to custom title.
+		$custom_title = $this->set_iframe_title( $item_id );
+		if ( ! empty( $custom_title ) ) {
+			$retval = str_replace( __( 'Embedded WordPress Post' ), esc_attr( $custom_title ), $retval );
+		}
+
+		// Remove default <blockquote>
+		$retval = substr( $retval, strpos( $retval, '</blockquote>' ) + 13 );
+
+		// Set up new fallback HTML
+		// @todo Maybe use KSES?
+		$fallback_html = $this->set_fallback_html( $item_id );
+
+		// Add our custom <blockquote>
+		return $fallback_html . $retval;
+	}
+
+	/**
+	 * Append our custom slug endpoint to oEmbed endpoint URL.
+	 *
+	 * Meant to be used as a filter on 'rest_url' before any call to
+	 * {@link get_oembed_endpoint_url()} is used.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @see add_oembed_discovery_links()
+	 *
+	 * @param  string $retval Current oEmbed endpoint URL
+	 * @return string
+	 */
+	function filter_rest_url( $retval = '' ) {
+		return $retval . "/{$this->slug_endpoint}";
+	}
+
+	/**
+	 * Inject activity content into the embed template.
+	 *
+	 * @since 2.5.0
+	 */
+	public function inject_content() {
+		$conditional = $this->page_conditional_fn;
+		if ( ! $conditional() ) {
+			return;
+		}
+
+		$this->content();
+	}
+}
\ No newline at end of file
diff --git src/bp-templates/bp-legacy/buddypress/embeds/activity.php src/bp-templates/bp-legacy/buddypress/embeds/activity.php
new file mode 100644
index 0000000..589fad2
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/activity.php
@@ -0,0 +1,26 @@
+
+		<?php if ( bp_has_activities( 'display_comments=threaded&show_hidden=true&include=' . bp_current_action() ) ) : ?>
+
+			<?php while ( bp_activities() ) : bp_the_activity(); ?>
+				<div class="wp-embed-excerpt"><?php bp_activity_content_body(); ?></div>
+
+				<p><a href="<?php bp_activity_thread_permalink(); ?>"><?php echo date_i18n( get_option( 'time_format' ) . ' - ' . get_option( 'date_format' ), strtotime( bp_get_activity_date_recorded() ) ); ?></a></p>
+
+				<?php
+				// Add inline JS for responsive IFRAMES if an embed is detected.
+				if ( bp_core_check_content_for_media_type( $GLOBALS['activities_template']->activity->content, 'embeds' ) ) {
+				?>
+
+<script>
+/*! fluidvids.js v1.2.0 | (c) 2013 @toddmotto | https://github.com/toddmotto/fluidvids */
+window.fluidvids=function(a,b){"use strict";var c=function(a){this.elem=a};c.prototype={init:function(){var a=100*(this.elem.height/this.elem.width);this.elem.style.position="absolute",this.elem.style.top="0",this.elem.style.left="0",this.elem.width="100%",this.elem.height="100%";var c=b.createElement("div");c.className="fluidvids",c.style.width="100%",c.style.position="relative",c.style.paddingTop=a+"%";var d=this.elem.parentNode;d.insertBefore(c,this.elem),c.appendChild(this.elem)}};for(var d=b.getElementsByTagName("iframe"),e=0;e<d.length;e++){d[e].src&&new c(d[e]).init()}}(window,document);
+</script>
+
+				<?php
+				}
+				?>
+
+			<?php endwhile; ?>
+
+		<?php endif; ?>
+
diff --git src/bp-templates/bp-legacy/buddypress/embeds/css.php src/bp-templates/bp-legacy/buddypress/embeds/css.php
new file mode 100644
index 0000000..129c877
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/css.php
@@ -0,0 +1,26 @@
+<style type="text/css">
+#wp-embed-header:after {
+	clear: both;
+	content: "";
+	display: table;
+	margin-bottom: 1em;
+}
+
+.wp-embed-avatar {
+	float: left;
+	margin: 0 .75em 0 0;
+}
+
+p.wp-embed-heading {
+	font-size: 16px;
+	margin-bottom: 0;
+}
+
+.wp-embed-excerpt {
+	margin-bottom: .5em;
+}
+
+.wp-embed-footer {
+	margin-top: 20px;
+}
+</style>
\ No newline at end of file
diff --git src/bp-templates/bp-legacy/buddypress/embeds/footer.php src/bp-templates/bp-legacy/buddypress/embeds/footer.php
new file mode 100644
index 0000000..22de696
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/footer.php
@@ -0,0 +1,33 @@
+			<div class="wp-embed-footer">
+				<div class="wp-embed-site-title">
+					<?php
+					$site_title = sprintf(
+						'<a href="%s"><img src="%s" srcset="%s 2x" width="32" height="32" alt="" class="wp-embed-site-icon"/><span>%s</span></a>',
+						esc_url( home_url() ),
+						esc_url( get_site_icon_url( 32, admin_url( 'images/w-logo-blue.png' ) ) ),
+						esc_url( get_site_icon_url( 64, admin_url( 'images/w-logo-blue.png' ) ) ),
+						esc_html( get_bloginfo( 'name' ) )
+					);
+
+					/**
+					 * Filter the site title HTML in the embed footer.
+					 *
+					 * @since 4.4.0
+					 *
+					 * @param string $site_title The site title HTML.
+					 */
+					echo apply_filters( 'embed_site_title_html', $site_title );
+					?>
+				</div>
+
+				<div class="wp-embed-meta">
+					<?php
+					/**
+					 * Print additional meta content in the embed template.
+					 *
+					 * @since 4.4.0
+					 */
+					do_action( 'embed_content_meta');
+					?>
+				</div>
+			</div>
\ No newline at end of file
diff --git src/bp-templates/bp-legacy/buddypress/embeds/header.php src/bp-templates/bp-legacy/buddypress/embeds/header.php
new file mode 100644
index 0000000..7a4bc8a
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/header.php
@@ -0,0 +1,18 @@
+
+		<div id="wp-embed-header">
+			<div class="wp-embed-avatar">
+				<a href="<?php bp_displayed_user_link(); ?>">
+					<?php bp_displayed_user_avatar( 'type=thumb&width=36&height=36' ); ?>
+				</a>
+			</div>
+
+			<p class="wp-embed-heading">
+				<a href="<?php bp_displayed_user_link(); ?>">
+					<?php bp_displayed_user_fullname(); ?>
+				</a>
+			</p>
+
+			<?php if ( bp_is_active( 'activity' ) && bp_activity_do_mentions() ) : ?>
+				<p class="wp-embed-mentionname">@<?php bp_displayed_user_mentionname(); ?></p>
+			<?php endif; ?>
+		</div>
diff --git src/bp-templates/bp-legacy/buddypress/embeds/template.php src/bp-templates/bp-legacy/buddypress/embeds/template.php
new file mode 100644
index 0000000..cb681df
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/template.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Generic embed template used by BuddyPress.
+ *
+ * When a BuddyPress item is embedded in an iframe, this file is used to
+ * create the output.
+ *
+ * This is a modified version of the bundled WordPress embed template
+ * included in WordPress 4.4+.
+ *
+ * @since 2.5.0
+ *
+ * @package BuddyPress
+ * @subpackage Embeds
+ */
+
+if ( ! headers_sent() ) {
+	header( 'X-WP-embed: true' );
+}
+
+?>
+<!DOCTYPE html>
+<html <?php language_attributes(); ?> class="no-js">
+<head>
+	<title><?php echo wp_get_document_title(); ?></title>
+	<meta http-equiv="X-UA-Compatible" content="IE=edge">
+	<base target="_top" />
+	<?php
+	/** This action is documented in wp-includes/embed-template.php */
+	do_action( 'embed_head' );
+
+	// This is used by r-a-y for testing purposes at the moment.
+	bp_get_template_part( 'embeds/css' );
+	?>
+</head>
+<body <?php body_class(); ?>>
+<div <?php post_class( 'wp-embed' ); ?>>
+<?php
+bp_get_template_part( 'embeds/header', bp_current_component() );
+
+/** This action is documented in wp-includes/embed-template.php */
+do_action( 'embed_content' );
+
+bp_get_template_part( 'embeds/footer', bp_current_component() );
+?>
+
+</div>
+
+<?php
+/** This action is documented in wp-includes/embed-template.php */
+do_action( 'embed_footer' );
+?>
+</body>
+</html>
\ No newline at end of file
