summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormiu <miu@chromium.org>2014-09-09 00:38:17 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-09 07:40:09 +0000
commit451dd9aa93c91692599bd2cc88f61373857d0b16 (patch)
tree505b9168e49d9826501a9330ae60d1ae39bc30ff
parentd89ef69a19b9015bf503f5f5d68d163d2ecd05de (diff)
downloadchromium_src-451dd9aa93c91692599bd2cc88f61373857d0b16.zip
chromium_src-451dd9aa93c91692599bd2cc88f61373857d0b16.tar.gz
chromium_src-451dd9aa93c91692599bd2cc88f61373857d0b16.tar.bz2
Site Isolation: RenderView-->RenderFrame for WebContentsVideoCaptureDevice and WebContentsAudioInputStream.
The tab capture audio/video devices will now track and respond to RenderFrame tree changes, rather than RenderView swaps, within a WebContents. In other words, content change events (navigation, crashes, etc.) are now followed by observing the render frame tree associated with a WebContents. AudioMirroringManager was mostly re-written around the idea that MirroringDestinations provide a query interface. The query interface was needed because AudioMirroringManager runs on the IO thread, while the question "Does render frame X associate with WebContents Y?" must be evaluated on the UI thread. Code de-duplication: Both WebContentsVideoCaptureDevice and WebContentsAudioInputStream now use the same tracking decision logic provided by WebContentsTracker. For the former, WebContentsTracker was made to detect when hops to the UI thread were not necessary, and will operate synchronously in that case. BUG=304341,392596 TBR=nick@chromium.org Review URL: https://codereview.chromium.org/542863004 Cr-Commit-Position: refs/heads/master@{#293888}
-rw-r--r--content/browser/media/capture/audio_mirroring_manager.cc232
-rw-r--r--content/browser/media/capture/audio_mirroring_manager.h133
-rw-r--r--content/browser/media/capture/audio_mirroring_manager_unittest.cc525
-rw-r--r--content/browser/media/capture/web_contents_audio_input_stream.cc104
-rw-r--r--content/browser/media/capture/web_contents_audio_input_stream.h13
-rw-r--r--content/browser/media/capture/web_contents_audio_input_stream_unittest.cc86
-rw-r--r--content/browser/media/capture/web_contents_tracker.cc117
-rw-r--r--content/browser/media/capture/web_contents_tracker.h90
-rw-r--r--content/browser/media/capture/web_contents_video_capture_device.cc214
-rw-r--r--content/browser/media/capture/web_contents_video_capture_device.h16
-rw-r--r--content/browser/renderer_host/media/audio_renderer_host.cc8
-rw-r--r--content/browser/renderer_host/media/audio_renderer_host_unittest.cc12
12 files changed, 993 insertions, 557 deletions
diff --git a/content/browser/media/capture/audio_mirroring_manager.cc b/content/browser/media/capture/audio_mirroring_manager.cc
index c9ba7ee..a75eb3c 100644
--- a/content/browser/media/capture/audio_mirroring_manager.cc
+++ b/content/browser/media/capture/audio_mirroring_manager.cc
@@ -4,6 +4,10 @@
#include "content/browser/media/capture/audio_mirroring_manager.h"
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
namespace content {
@@ -29,116 +33,182 @@ AudioMirroringManager::AudioMirroringManager() {
AudioMirroringManager::~AudioMirroringManager() {}
void AudioMirroringManager::AddDiverter(
- int render_process_id, int render_view_id, Diverter* diverter) {
+ int render_process_id, int render_frame_id, Diverter* diverter) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(diverter);
- // DCHECK(diverter not already in diverters_ under any key)
+ // DCHECK(diverter not already in routes_)
#ifndef NDEBUG
- for (DiverterMap::const_iterator it = diverters_.begin();
- it != diverters_.end(); ++it) {
- DCHECK_NE(diverter, it->second);
+ for (StreamRoutes::const_iterator it = routes_.begin();
+ it != routes_.end(); ++it) {
+ DCHECK_NE(diverter, it->diverter);
}
#endif
-
- // Add the diverter to the set of active diverters.
- const Target target(render_process_id, render_view_id);
- diverters_.insert(std::make_pair(target, diverter));
-
- // If a mirroring session is active, start diverting the audio stream
- // immediately.
- SessionMap::iterator session_it = sessions_.find(target);
- if (session_it != sessions_.end()) {
- diverter->StartDiverting(
- session_it->second->AddInput(diverter->GetAudioParameters()));
- }
+ routes_.push_back(StreamRoutingState(
+ SourceFrameRef(render_process_id, render_frame_id),
+ diverter));
+
+ // Query existing destinations to see whether to immediately start diverting
+ // the stream.
+ std::set<SourceFrameRef> candidates;
+ candidates.insert(routes_.back().source_render_frame);
+ InitiateQueriesToFindNewDestination(NULL, candidates);
}
-void AudioMirroringManager::RemoveDiverter(
- int render_process_id, int render_view_id, Diverter* diverter) {
+void AudioMirroringManager::RemoveDiverter(Diverter* diverter) {
DCHECK(thread_checker_.CalledOnValidThread());
- // Stop diverting the audio stream if a mirroring session is active.
- const Target target(render_process_id, render_view_id);
- SessionMap::iterator session_it = sessions_.find(target);
- if (session_it != sessions_.end())
- diverter->StopDiverting();
-
- // Remove the diverter from the set of active diverters.
- for (DiverterMap::iterator it = diverters_.lower_bound(target);
- it != diverters_.end() && it->first == target; ++it) {
- if (it->second == diverter) {
- diverters_.erase(it);
- break;
+ // Find and remove the entry from the routing table. If the stream is being
+ // diverted, it is stopped.
+ for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) {
+ if (it->diverter == diverter) {
+ ChangeRoute(&(*it), NULL);
+ routes_.erase(it);
+ return;
}
}
+ NOTREACHED();
}
-void AudioMirroringManager::StartMirroring(
- int render_process_id, int render_view_id,
- MirroringDestination* destination) {
+void AudioMirroringManager::StartMirroring(MirroringDestination* destination) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(destination);
- // Insert an entry into the set of active mirroring sessions. If a mirroring
- // session is already active for |render_process_id| + |render_view_id|,
- // replace the entry.
- const Target target(render_process_id, render_view_id);
- SessionMap::iterator session_it = sessions_.find(target);
- MirroringDestination* old_destination;
- if (session_it == sessions_.end()) {
- old_destination = NULL;
- sessions_.insert(std::make_pair(target, destination));
-
- DVLOG(1) << "Start mirroring render_process_id:render_view_id="
- << render_process_id << ':' << render_view_id
- << " --> MirroringDestination@" << destination;
- } else {
- old_destination = session_it->second;
- session_it->second = destination;
-
- DVLOG(1) << "Switch mirroring of render_process_id:render_view_id="
- << render_process_id << ':' << render_view_id
- << " MirroringDestination@" << old_destination
- << " --> MirroringDestination@" << destination;
+ // Insert an entry into the set of active mirroring sessions, if this is a
+ // previously-unknown destination.
+ if (std::find(sessions_.begin(), sessions_.end(), destination) ==
+ sessions_.end()) {
+ sessions_.push_back(destination);
}
- // Divert audio streams coming from |target| to |destination|. If streams
- // were already diverted to the |old_destination|, remove them.
- for (DiverterMap::iterator it = diverters_.lower_bound(target);
- it != diverters_.end() && it->first == target; ++it) {
- Diverter* const diverter = it->second;
- if (old_destination)
- diverter->StopDiverting();
- diverter->StartDiverting(
- destination->AddInput(diverter->GetAudioParameters()));
+ // Query the MirroringDestination to see which of the audio streams should be
+ // diverted.
+ std::set<SourceFrameRef> candidates;
+ for (StreamRoutes::const_iterator it = routes_.begin(); it != routes_.end();
+ ++it) {
+ if (!it->destination || it->destination == destination)
+ candidates.insert(it->source_render_frame);
+ }
+ if (!candidates.empty()) {
+ destination->QueryForMatches(
+ candidates,
+ base::Bind(&AudioMirroringManager::UpdateRoutesToDestination,
+ base::Unretained(this),
+ destination,
+ false));
}
}
-void AudioMirroringManager::StopMirroring(
- int render_process_id, int render_view_id,
- MirroringDestination* destination) {
+void AudioMirroringManager::StopMirroring(MirroringDestination* destination) {
DCHECK(thread_checker_.CalledOnValidThread());
- // Stop mirroring if there is an active session *and* the destination
- // matches.
- const Target target(render_process_id, render_view_id);
- SessionMap::iterator session_it = sessions_.find(target);
- if (session_it == sessions_.end() || destination != session_it->second)
+ // Stop diverting each audio stream in the mirroring session being stopped.
+ // Each stopped stream becomes a candidate to be diverted to another
+ // destination.
+ std::set<SourceFrameRef> redivert_candidates;
+ for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) {
+ if (it->destination == destination) {
+ ChangeRoute(&(*it), NULL);
+ redivert_candidates.insert(it->source_render_frame);
+ }
+ }
+ if (!redivert_candidates.empty())
+ InitiateQueriesToFindNewDestination(destination, redivert_candidates);
+
+ // Remove the entry from the set of active mirroring sessions.
+ const Destinations::iterator dest_it =
+ std::find(sessions_.begin(), sessions_.end(), destination);
+ if (dest_it == sessions_.end()) {
+ NOTREACHED();
return;
+ }
+ sessions_.erase(dest_it);
+}
- DVLOG(1) << "Stop mirroring render_process_id:render_view_id="
- << render_process_id << ':' << render_view_id
- << " --> MirroringDestination@" << destination;
+void AudioMirroringManager::InitiateQueriesToFindNewDestination(
+ MirroringDestination* old_destination,
+ const std::set<SourceFrameRef>& candidates) {
+ DCHECK(thread_checker_.CalledOnValidThread());
- // Stop diverting each audio stream in the mirroring session being stopped.
- for (DiverterMap::iterator it = diverters_.lower_bound(target);
- it != diverters_.end() && it->first == target; ++it) {
- it->second->StopDiverting();
+ for (Destinations::const_iterator it = sessions_.begin();
+ it != sessions_.end(); ++it) {
+ if (*it != old_destination) {
+ (*it)->QueryForMatches(
+ candidates,
+ base::Bind(&AudioMirroringManager::UpdateRoutesToDestination,
+ base::Unretained(this),
+ *it,
+ true));
+ }
}
+}
- // Remove the entry from the set of active mirroring sessions.
- sessions_.erase(session_it);
+void AudioMirroringManager::UpdateRoutesToDestination(
+ MirroringDestination* destination,
+ bool add_only,
+ const std::set<SourceFrameRef>& matches) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (std::find(sessions_.begin(), sessions_.end(), destination) ==
+ sessions_.end()) {
+ return; // Query result callback invoked after StopMirroring().
+ }
+
+ DVLOG(1) << (add_only ? "Add " : "Replace with ") << matches.size()
+ << " routes to MirroringDestination@" << destination;
+
+ // Start/stop diverting based on |matches|. Any stopped stream becomes a
+ // candidate to be diverted to another destination.
+ std::set<SourceFrameRef> redivert_candidates;
+ for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) {
+ if (matches.find(it->source_render_frame) != matches.end()) {
+ // Only change the route if the stream is not already being diverted.
+ if (!it->destination)
+ ChangeRoute(&(*it), destination);
+ } else if (!add_only) {
+ // Only stop diverting if the stream is currently routed to |destination|.
+ if (it->destination == destination) {
+ ChangeRoute(&(*it), NULL);
+ redivert_candidates.insert(it->source_render_frame);
+ }
+ }
+ }
+ if (!redivert_candidates.empty())
+ InitiateQueriesToFindNewDestination(destination, redivert_candidates);
}
+// static
+void AudioMirroringManager::ChangeRoute(
+ StreamRoutingState* route, MirroringDestination* new_destination) {
+ if (route->destination == new_destination)
+ return; // No change.
+
+ if (route->destination) {
+ DVLOG(1) << "Stop diverting render_process_id:render_frame_id="
+ << route->source_render_frame.first << ':'
+ << route->source_render_frame.second
+ << " --> MirroringDestination@" << route->destination;
+ route->diverter->StopDiverting();
+ route->destination = NULL;
+ }
+
+ if (new_destination) {
+ DVLOG(1) << "Start diverting of render_process_id:render_frame_id="
+ << route->source_render_frame.first << ':'
+ << route->source_render_frame.second
+ << " --> MirroringDestination@" << new_destination;
+ route->diverter->StartDiverting(
+ new_destination->AddInput(route->diverter->GetAudioParameters()));
+ route->destination = new_destination;
+ }
+}
+
+AudioMirroringManager::StreamRoutingState::StreamRoutingState(
+ const SourceFrameRef& source_frame, Diverter* stream_diverter)
+ : source_render_frame(source_frame),
+ diverter(stream_diverter),
+ destination(NULL) {}
+
+AudioMirroringManager::StreamRoutingState::~StreamRoutingState() {}
+
} // namespace content
diff --git a/content/browser/media/capture/audio_mirroring_manager.h b/content/browser/media/capture/audio_mirroring_manager.h
index 86a715e..441c884 100644
--- a/content/browser/media/capture/audio_mirroring_manager.h
+++ b/content/browser/media/capture/audio_mirroring_manager.h
@@ -11,26 +11,33 @@
//
// 1. AudioRendererHost gets a CreateStream message from the render process
// and, among other things, creates an AudioOutputController to control the
-// audio data flow between the render and browser processes.
-// 2. At some point, AudioRendererHost receives an "associate with render
-// view" message. Among other actions, it registers the
-// AudioOutputController with AudioMirroringManager (as a Diverter).
-// 3. A user request to mirror all the audio for a single RenderView is made.
-// A MirroringDestination is created, and StartMirroring() is called to
-// begin the mirroring session. This causes AudioMirroringManager to
-// instruct any matching Diverters to divert their audio data to the
-// MirroringDestination.
-//
-// #2 and #3 above may occur in any order, as it is the job of
-// AudioMirroringManager to realize when the players can be "matched up."
+// audio data flow between the render and browser processes. More
+// importantly, it registers the AudioOutputController with
+// AudioMirroringManager (as a Diverter).
+// 2. A user request to mirror all the audio for a WebContents is made. A
+// MirroringDestination is created, and StartMirroring() is called to begin
+// the mirroring session. The MirroringDestination is queried to determine
+// which of all the known Diverters will re-route their audio to it.
+// 3a. During a mirroring session, AudioMirroringManager may encounter new
+// Diverters, and will query all the MirroringDestinations to determine
+// which is a match, if any.
+// 3b. During a mirroring session, a call to StartMirroring() can be made to
+// request a "refresh" query on a MirroringDestination, and this will
+// result in AudioMirroringManager starting/stopping only those Diverters
+// that are not correctly routed to the destination.
+// 3c. When a mirroring session is stopped, the remaining destinations will be
+// queried to determine whether diverting should continue to a different
+// destination.
#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_AUDIO_MIRRORING_MANAGER_H_
#define CONTENT_BROWSER_MEDIA_CAPTURE_AUDIO_MIRRORING_MANAGER_H_
-#include <map>
+#include <set>
#include <utility>
+#include <vector>
#include "base/basictypes.h"
+#include "base/callback_forward.h"
#include "base/threading/thread_checker.h"
#include "content/common/content_export.h"
#include "media/audio/audio_source_diverter.h"
@@ -46,10 +53,25 @@ class CONTENT_EXPORT AudioMirroringManager {
// Interface for diverting audio data to an alternative AudioOutputStream.
typedef media::AudioSourceDiverter Diverter;
+ // A SourceFrameRef is a RenderFrameHost identified by a <render_process_id,
+ // render_frame_id> pair.
+ typedef std::pair<int, int> SourceFrameRef;
+
// Interface to be implemented by audio mirroring destinations. See comments
// for StartMirroring() and StopMirroring() below.
class MirroringDestination {
public:
+ // Asynchronously query whether this MirroringDestination wants to consume
+ // audio sourced from each of the |candidates|. |results_callback| is run
+ // to indicate which of them (or none) should have audio routed to this
+ // MirroringDestination. |results_callback| must be run on the same thread
+ // as the one that called QueryForMatches().
+ typedef base::Callback<void(const std::set<SourceFrameRef>&)>
+ MatchesCallback;
+ virtual void QueryForMatches(
+ const std::set<SourceFrameRef>& candidates,
+ const MatchesCallback& results_callback) = 0;
+
// Create a consumer of audio data in the format specified by |params|, and
// connect it as an input to mirroring. When Close() is called on the
// returned AudioOutputStream, the input is disconnected and the object
@@ -68,41 +90,68 @@ class CONTENT_EXPORT AudioMirroringManager {
// Returns the global instance.
static AudioMirroringManager* GetInstance();
- // Add/Remove a diverter for an audio stream with a known RenderView target
- // (represented by |render_process_id| + |render_view_id|). Multiple
- // diverters may be added for the same target. |diverter| must live until
- // after RemoveDiverter() is called.
- //
- // Re-entrancy warning: These methods should not be called by a Diverter
- // during a Start/StopDiverting() invocation.
- virtual void AddDiverter(int render_process_id, int render_view_id,
+ // Add/Remove a diverter for an audio stream with a known RenderFrame source
+ // (represented by |render_process_id| + |render_frame_id|). Multiple
+ // diverters may be added for the same source frame, but never the same
+ // diverter. |diverter| must live until after RemoveDiverter() is called.
+ virtual void AddDiverter(int render_process_id, int render_frame_id,
Diverter* diverter);
- virtual void RemoveDiverter(int render_process_id, int render_view_id,
- Diverter* diverter);
-
- // Start/stop mirroring all audio output streams associated with a RenderView
- // target (represented by |render_process_id| + |render_view_id|) to
- // |destination|. |destination| must live until after StopMirroring() is
- // called.
- virtual void StartMirroring(int render_process_id, int render_view_id,
- MirroringDestination* destination);
- virtual void StopMirroring(int render_process_id, int render_view_id,
- MirroringDestination* destination);
+ virtual void RemoveDiverter(Diverter* diverter);
+
+ // (Re-)Start/Stop mirroring to the given |destination|. |destination| must
+ // live until after StopMirroring() is called. A client may request a
+ // re-start by calling StartMirroring() again; and this will cause
+ // AudioMirroringManager to query |destination| and only re-route those
+ // diverters that are missing/new to the returned set of matches.
+ virtual void StartMirroring(MirroringDestination* destination);
+ virtual void StopMirroring(MirroringDestination* destination);
private:
- // A mirroring target is a RenderView identified by a
- // <render_process_id, render_view_id> pair.
- typedef std::pair<int, int> Target;
+ friend class AudioMirroringManagerTest;
- // Note: Objects in these maps are not owned.
- typedef std::multimap<Target, Diverter*> DiverterMap;
- typedef std::map<Target, MirroringDestination*> SessionMap;
+ struct StreamRoutingState {
+ // The source render frame associated with the audio stream.
+ SourceFrameRef source_render_frame;
- // Currently-active divertable audio streams.
- DiverterMap diverters_;
+ // The diverter for re-routing the audio stream.
+ Diverter* diverter;
+
+ // If not NULL, the audio stream is currently being diverted to this
+ // destination.
+ MirroringDestination* destination;
+
+ StreamRoutingState(const SourceFrameRef& source_frame,
+ Diverter* stream_diverter);
+ ~StreamRoutingState();
+ };
- // Currently-active mirroring sessions.
- SessionMap sessions_;
+ typedef std::vector<StreamRoutingState> StreamRoutes;
+ typedef std::vector<MirroringDestination*> Destinations;
+
+ // Helper to find a destination other than |old_destination| for the given
+ // |candidates| to be diverted to.
+ void InitiateQueriesToFindNewDestination(
+ MirroringDestination* old_destination,
+ const std::set<SourceFrameRef>& candidates);
+
+ // MirroringDestination query callback. |matches| contains all RenderFrame
+ // sources that will be diverted to |destination|. If |add_only| is false,
+ // then any Diverters currently routed to |destination| but not found in
+ // |matches| will be stopped.
+ void UpdateRoutesToDestination(MirroringDestination* destination,
+ bool add_only,
+ const std::set<SourceFrameRef>& matches);
+
+ // Starts diverting audio to the |new_destination|, if not NULL. Otherwise,
+ // stops diverting audio.
+ static void ChangeRoute(StreamRoutingState* route,
+ MirroringDestination* new_destination);
+
+ // Routing table. Contains one entry for each Diverter.
+ StreamRoutes routes_;
+
+ // All active mirroring sessions.
+ Destinations sessions_;
// Used to check that all AudioMirroringManager code runs on the same thread.
base::ThreadChecker thread_checker_;
diff --git a/content/browser/media/capture/audio_mirroring_manager_unittest.cc b/content/browser/media/capture/audio_mirroring_manager_unittest.cc
index cd19776..6fece29 100644
--- a/content/browser/media/capture/audio_mirroring_manager_unittest.cc
+++ b/content/browser/media/capture/audio_mirroring_manager_unittest.cc
@@ -5,6 +5,7 @@
#include "content/browser/media/capture/audio_mirroring_manager.h"
#include <map>
+#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -18,6 +19,7 @@
using media::AudioOutputStream;
using media::AudioParameters;
using testing::_;
+using testing::Invoke;
using testing::NotNull;
using testing::Ref;
using testing::Return;
@@ -37,14 +39,56 @@ class MockDiverter : public AudioMirroringManager::Diverter {
class MockMirroringDestination
: public AudioMirroringManager::MirroringDestination {
public:
+ typedef AudioMirroringManager::SourceFrameRef SourceFrameRef;
+
+ MockMirroringDestination(int render_process_id, int render_frame_id)
+ : render_process_id_(render_process_id),
+ render_frame_id_(render_frame_id),
+ query_count_(0) {}
+
+ MOCK_METHOD2(QueryForMatches,
+ void(const std::set<SourceFrameRef>& candidates,
+ const MatchesCallback& results_callback));
MOCK_METHOD1(AddInput,
media::AudioOutputStream*(const media::AudioParameters& params));
+
+ void SimulateQuery(const std::set<SourceFrameRef>& candidates,
+ const MatchesCallback& results_callback) {
+ ++query_count_;
+
+ std::set<SourceFrameRef> result;
+ if (candidates.find(SourceFrameRef(render_process_id_, render_frame_id_)) !=
+ candidates.end()) {
+ result.insert(SourceFrameRef(render_process_id_, render_frame_id_));
+ }
+ results_callback.Run(result);
+ }
+
+ media::AudioOutputStream* SimulateAddInput(
+ const media::AudioParameters& params) {
+ static AudioOutputStream* const kNonNullPointer =
+ reinterpret_cast<AudioOutputStream*>(0x11111110);
+ return kNonNullPointer;
+ }
+
+ int query_count() const {
+ return query_count_;
+ }
+
+ private:
+ const int render_process_id_;
+ const int render_frame_id_;
+ int query_count_;
};
} // namespace
class AudioMirroringManagerTest : public testing::Test {
public:
+ typedef AudioMirroringManager::Diverter Diverter;
+ typedef AudioMirroringManager::MirroringDestination MirroringDestination;
+ typedef AudioMirroringManager::StreamRoutes StreamRoutes;
+
AudioMirroringManagerTest()
: io_thread_(BrowserThread::IO, &message_loop_),
params_(AudioParameters::AUDIO_FAKE, media::CHANNEL_LAYOUT_STEREO,
@@ -52,7 +96,7 @@ class AudioMirroringManagerTest : public testing::Test {
AudioParameters::kAudioCDSampleRate / 10) {}
MockDiverter* CreateStream(
- int render_process_id, int render_view_id, int expected_times_diverted) {
+ int render_process_id, int render_frame_id, int expected_times_diverted) {
MockDiverter* const diverter = new MockDiverter();
if (expected_times_diverted > 0) {
EXPECT_CALL(*diverter, GetAudioParameters())
@@ -64,41 +108,52 @@ class AudioMirroringManagerTest : public testing::Test {
.Times(expected_times_diverted);
}
- mirroring_manager_.AddDiverter(render_process_id, render_view_id, diverter);
+ mirroring_manager_.AddDiverter(
+ render_process_id, render_frame_id, diverter);
return diverter;
}
- void KillStream(
- int render_process_id, int render_view_id, MockDiverter* diverter) {
- mirroring_manager_.RemoveDiverter(
- render_process_id, render_view_id, diverter);
-
+ void KillStream(MockDiverter* diverter) {
+ mirroring_manager_.RemoveDiverter(diverter);
delete diverter;
}
- MockMirroringDestination* StartMirroringTo(
- int render_process_id, int render_view_id, int expected_inputs_added) {
- MockMirroringDestination* const dest = new MockMirroringDestination();
+ void StartMirroringTo(const scoped_ptr<MockMirroringDestination>& dest,
+ int expected_inputs_added) {
+ EXPECT_CALL(*dest, QueryForMatches(_, _))
+ .WillRepeatedly(Invoke(dest.get(),
+ &MockMirroringDestination::SimulateQuery));
if (expected_inputs_added > 0) {
- static AudioOutputStream* const kNonNullPointer =
- reinterpret_cast<AudioOutputStream*>(0x11111110);
EXPECT_CALL(*dest, AddInput(Ref(params_)))
.Times(expected_inputs_added)
- .WillRepeatedly(Return(kNonNullPointer));
+ .WillRepeatedly(Invoke(dest.get(),
+ &MockMirroringDestination::SimulateAddInput))
+ .RetiresOnSaturation();
}
- mirroring_manager_.StartMirroring(render_process_id, render_view_id, dest);
+ mirroring_manager_.StartMirroring(dest.get());
+ }
- return dest;
+ void StopMirroringTo(const scoped_ptr<MockMirroringDestination>& dest) {
+ mirroring_manager_.StopMirroring(dest.get());
}
- void StopMirroringTo(int render_process_id, int render_view_id,
- MockMirroringDestination* dest) {
- mirroring_manager_.StopMirroring(render_process_id, render_view_id, dest);
+ int CountStreamsDivertedTo(
+ const scoped_ptr<MockMirroringDestination>& dest) const {
+ int count = 0;
+ for (StreamRoutes::const_iterator it = mirroring_manager_.routes_.begin();
+ it != mirroring_manager_.routes_.end(); ++it) {
+ if (it->destination == dest.get())
+ ++count;
+ }
+ return count;
+ }
- delete dest;
-}
+ void ExpectNoLongerManagingAnything() const {
+ EXPECT_TRUE(mirroring_manager_.routes_.empty());
+ EXPECT_TRUE(mirroring_manager_.sessions_.empty());
+ }
private:
base::MessageLoopForIO message_loop_;
@@ -111,123 +166,389 @@ class AudioMirroringManagerTest : public testing::Test {
namespace {
const int kRenderProcessId = 123;
-const int kRenderViewId = 456;
+const int kRenderFrameId = 456;
const int kAnotherRenderProcessId = 789;
-const int kAnotherRenderViewId = 1234;
+const int kAnotherRenderFrameId = 1234;
const int kYetAnotherRenderProcessId = 4560;
-const int kYetAnotherRenderViewId = 7890;
+const int kYetAnotherRenderFrameId = 7890;
}
TEST_F(AudioMirroringManagerTest, MirroringSessionOfNothing) {
- MockMirroringDestination* const destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 0);
- StopMirroringTo(kRenderProcessId, kRenderViewId, destination);
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 0);
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(0, destination->query_count());
+
+ ExpectNoLongerManagingAnything();
}
TEST_F(AudioMirroringManagerTest, TwoMirroringSessionsOfNothing) {
- MockMirroringDestination* const destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 0);
- StopMirroringTo(kRenderProcessId, kRenderViewId, destination);
-
- MockMirroringDestination* const another_destination =
- StartMirroringTo(kAnotherRenderProcessId, kAnotherRenderViewId, 0);
- StopMirroringTo(kAnotherRenderProcessId, kAnotherRenderViewId,
- another_destination);
-}
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 0);
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(0, destination->query_count());
-TEST_F(AudioMirroringManagerTest, SwitchMirroringDestinationNoStreams) {
- MockMirroringDestination* const destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 0);
- MockMirroringDestination* const new_destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 0);
- StopMirroringTo(kRenderProcessId, kRenderViewId, destination);
- StopMirroringTo(kRenderProcessId, kRenderViewId, new_destination);
+ const scoped_ptr<MockMirroringDestination> another_destination(
+ new MockMirroringDestination(kAnotherRenderProcessId,
+ kAnotherRenderFrameId));
+ StartMirroringTo(another_destination, 0);
+ EXPECT_EQ(0, CountStreamsDivertedTo(another_destination));
+
+ StopMirroringTo(another_destination);
+ EXPECT_EQ(0, another_destination->query_count());
+
+ ExpectNoLongerManagingAnything();
}
+// Tests that a mirroring session starts after, and ends before, a stream that
+// will be diverted to it.
TEST_F(AudioMirroringManagerTest, StreamLifetimeAroundMirroringSession) {
- MockDiverter* const stream = CreateStream(kRenderProcessId, kRenderViewId, 1);
- MockMirroringDestination* const destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 1);
- StopMirroringTo(kRenderProcessId, kRenderViewId, destination);
- KillStream(kRenderProcessId, kRenderViewId, stream);
+ MockDiverter* const stream =
+ CreateStream(kRenderProcessId, kRenderFrameId, 1);
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ KillStream(stream);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ ExpectNoLongerManagingAnything();
}
+// Tests that a mirroring session starts before, and ends after, a stream that
+// will be diverted to it.
TEST_F(AudioMirroringManagerTest, StreamLifetimeWithinMirroringSession) {
- MockMirroringDestination* const destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 1);
- MockDiverter* const stream = CreateStream(kRenderProcessId, kRenderViewId, 1);
- KillStream(kRenderProcessId, kRenderViewId, stream);
- StopMirroringTo(kRenderProcessId, kRenderViewId, destination);
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 1);
+ EXPECT_EQ(0, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ MockDiverter* const stream =
+ CreateStream(kRenderProcessId, kRenderFrameId, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+
+ KillStream(stream);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ ExpectNoLongerManagingAnything();
}
-TEST_F(AudioMirroringManagerTest, StreamLifetimeAroundTwoMirroringSessions) {
- MockDiverter* const stream = CreateStream(kRenderProcessId, kRenderViewId, 2);
- MockMirroringDestination* const destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 1);
- StopMirroringTo(kRenderProcessId, kRenderViewId, destination);
- MockMirroringDestination* const new_destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 1);
- StopMirroringTo(kRenderProcessId, kRenderViewId, new_destination);
- KillStream(kRenderProcessId, kRenderViewId, stream);
+// Tests that a stream is diverted correctly as two mirroring sessions come and
+// go.
+TEST_F(AudioMirroringManagerTest, StreamLifetimeAcrossTwoMirroringSessions) {
+ MockDiverter* const stream =
+ CreateStream(kRenderProcessId, kRenderFrameId, 2);
+
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ const scoped_ptr<MockMirroringDestination> second_destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(second_destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, second_destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(second_destination));
+
+ StopMirroringTo(second_destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, second_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(second_destination));
+
+ KillStream(stream);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, second_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(second_destination));
+
+ ExpectNoLongerManagingAnything();
}
-TEST_F(AudioMirroringManagerTest, StreamLifetimeWithinTwoMirroringSessions) {
- MockMirroringDestination* const destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 1);
- MockDiverter* const stream = CreateStream(kRenderProcessId, kRenderViewId, 2);
- StopMirroringTo(kRenderProcessId, kRenderViewId, destination);
- MockMirroringDestination* const new_destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 1);
- KillStream(kRenderProcessId, kRenderViewId, stream);
- StopMirroringTo(kRenderProcessId, kRenderViewId, new_destination);
+// Tests that a stream does not flip-flop between two destinations that are a
+// match for it.
+TEST_F(AudioMirroringManagerTest, StreamDivertingStickyToOneDestination_1) {
+ MockDiverter* const stream =
+ CreateStream(kRenderProcessId, kRenderFrameId, 2);
+
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+
+ const scoped_ptr<MockMirroringDestination> replacement_destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(replacement_destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(0, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, replacement_destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(replacement_destination));
+
+ StopMirroringTo(replacement_destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ KillStream(stream);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ ExpectNoLongerManagingAnything();
+}
+
+// Same as StreamDivertingStickyToOneDestination_1, with a different order of
+// operations that should have the same effects.
+TEST_F(AudioMirroringManagerTest, StreamDivertingStickyToOneDestination_2) {
+ MockDiverter* const stream =
+ CreateStream(kRenderProcessId, kRenderFrameId, 2);
+
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+
+ const scoped_ptr<MockMirroringDestination> replacement_destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(replacement_destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(0, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, replacement_destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(replacement_destination));
+
+ KillStream(stream);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ StopMirroringTo(replacement_destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ ExpectNoLongerManagingAnything();
+}
+
+// Same as StreamDivertingStickyToOneDestination_1, except that the stream is
+// killed before the first destination is stopped. Therefore, the second
+// destination should never see the stream.
+TEST_F(AudioMirroringManagerTest, StreamDivertingStickyToOneDestination_3) {
+ MockDiverter* const stream =
+ CreateStream(kRenderProcessId, kRenderFrameId, 1);
+
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+
+ const scoped_ptr<MockMirroringDestination> replacement_destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(replacement_destination, 0);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(0, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ KillStream(stream);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(0, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(0, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ StopMirroringTo(replacement_destination);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(0, replacement_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(replacement_destination));
+
+ ExpectNoLongerManagingAnything();
}
+// Tests that multiple streams are diverted/mixed to one destination.
TEST_F(AudioMirroringManagerTest, MultipleStreamsInOneMirroringSession) {
MockDiverter* const stream1 =
- CreateStream(kRenderProcessId, kRenderViewId, 1);
- MockMirroringDestination* const destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 3);
+ CreateStream(kRenderProcessId, kRenderFrameId, 1);
+
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 3);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+
MockDiverter* const stream2 =
- CreateStream(kRenderProcessId, kRenderViewId, 1);
+ CreateStream(kRenderProcessId, kRenderFrameId, 1);
+ EXPECT_EQ(2, destination->query_count());
+ EXPECT_EQ(2, CountStreamsDivertedTo(destination));
+
MockDiverter* const stream3 =
- CreateStream(kRenderProcessId, kRenderViewId, 1);
- KillStream(kRenderProcessId, kRenderViewId, stream2);
- StopMirroringTo(kRenderProcessId, kRenderViewId, destination);
- KillStream(kRenderProcessId, kRenderViewId, stream3);
- KillStream(kRenderProcessId, kRenderViewId, stream1);
+ CreateStream(kRenderProcessId, kRenderFrameId, 1);
+ EXPECT_EQ(3, destination->query_count());
+ EXPECT_EQ(3, CountStreamsDivertedTo(destination));
+
+ KillStream(stream2);
+ EXPECT_EQ(3, destination->query_count());
+ EXPECT_EQ(2, CountStreamsDivertedTo(destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(3, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ KillStream(stream3);
+ EXPECT_EQ(3, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ KillStream(stream1);
+ EXPECT_EQ(3, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+
+ ExpectNoLongerManagingAnything();
}
// A random interleaving of operations for three separate targets, each of which
// has one stream mirrored to one destination.
TEST_F(AudioMirroringManagerTest, ThreeSeparateMirroringSessions) {
MockDiverter* const stream =
- CreateStream(kRenderProcessId, kRenderViewId, 1);
- MockMirroringDestination* const destination =
- StartMirroringTo(kRenderProcessId, kRenderViewId, 1);
+ CreateStream(kRenderProcessId, kRenderFrameId, 1);
+
+ const scoped_ptr<MockMirroringDestination> destination(
+ new MockMirroringDestination(kRenderProcessId, kRenderFrameId));
+ StartMirroringTo(destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+
+ const scoped_ptr<MockMirroringDestination> another_destination(
+ new MockMirroringDestination(kAnotherRenderProcessId,
+ kAnotherRenderFrameId));
+ StartMirroringTo(another_destination, 1);
+ EXPECT_EQ(1, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(0, another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(another_destination));
- MockMirroringDestination* const another_destination =
- StartMirroringTo(kAnotherRenderProcessId, kAnotherRenderViewId, 1);
MockDiverter* const another_stream =
- CreateStream(kAnotherRenderProcessId, kAnotherRenderViewId, 1);
-
- KillStream(kRenderProcessId, kRenderViewId, stream);
+ CreateStream(kAnotherRenderProcessId, kAnotherRenderFrameId, 1);
+ EXPECT_EQ(2, destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, another_destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(another_destination));
+
+ KillStream(stream);
+ EXPECT_EQ(2, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(1, another_destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(another_destination));
MockDiverter* const yet_another_stream =
- CreateStream(kYetAnotherRenderProcessId, kYetAnotherRenderViewId, 1);
- MockMirroringDestination* const yet_another_destination =
- StartMirroringTo(kYetAnotherRenderProcessId, kYetAnotherRenderViewId, 1);
-
- StopMirroringTo(kAnotherRenderProcessId, kAnotherRenderViewId,
- another_destination);
-
- StopMirroringTo(kYetAnotherRenderProcessId, kYetAnotherRenderViewId,
- yet_another_destination);
-
- StopMirroringTo(kRenderProcessId, kRenderViewId, destination);
-
- KillStream(kAnotherRenderProcessId, kAnotherRenderViewId, another_stream);
- KillStream(kYetAnotherRenderProcessId, kYetAnotherRenderViewId,
- yet_another_stream);
+ CreateStream(kYetAnotherRenderProcessId, kYetAnotherRenderFrameId, 1);
+ EXPECT_EQ(3, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(2, another_destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(another_destination));
+
+ const scoped_ptr<MockMirroringDestination> yet_another_destination(
+ new MockMirroringDestination(kYetAnotherRenderProcessId,
+ kYetAnotherRenderFrameId));
+ StartMirroringTo(yet_another_destination, 1);
+ EXPECT_EQ(3, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(2, another_destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(another_destination));
+ EXPECT_EQ(1, yet_another_destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(yet_another_destination));
+
+ StopMirroringTo(another_destination);
+ EXPECT_EQ(4, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(2, another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(another_destination));
+ EXPECT_EQ(2, yet_another_destination->query_count());
+ EXPECT_EQ(1, CountStreamsDivertedTo(yet_another_destination));
+
+ StopMirroringTo(yet_another_destination);
+ EXPECT_EQ(5, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(2, another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(another_destination));
+ EXPECT_EQ(2, yet_another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(yet_another_destination));
+
+ StopMirroringTo(destination);
+ EXPECT_EQ(5, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(2, another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(another_destination));
+ EXPECT_EQ(2, yet_another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(yet_another_destination));
+
+ KillStream(another_stream);
+ EXPECT_EQ(5, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(2, another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(another_destination));
+ EXPECT_EQ(2, yet_another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(yet_another_destination));
+
+ KillStream(yet_another_stream);
+ EXPECT_EQ(5, destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(destination));
+ EXPECT_EQ(2, another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(another_destination));
+ EXPECT_EQ(2, yet_another_destination->query_count());
+ EXPECT_EQ(0, CountStreamsDivertedTo(yet_another_destination));
+
+ ExpectNoLongerManagingAnything();
}
} // namespace content
diff --git a/content/browser/media/capture/web_contents_audio_input_stream.cc b/content/browser/media/capture/web_contents_audio_input_stream.cc
index 9d42c2d..55eee22 100644
--- a/content/browser/media/capture/web_contents_audio_input_stream.cc
+++ b/content/browser/media/capture/web_contents_audio_input_stream.cc
@@ -15,8 +15,11 @@
#include "content/browser/media/capture/web_contents_capture_util.h"
#include "content/browser/media/capture/web_contents_tracker.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
#include "media/audio/virtual_audio_input_stream.h"
#include "media/audio/virtual_audio_output_stream.h"
+#include "media/base/bind_to_current_loop.h"
namespace content {
@@ -52,6 +55,8 @@ class WebContentsAudioInputStream::Impl
private:
friend class base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>;
+ typedef AudioMirroringManager::SourceFrameRef SourceFrameRef;
+
enum State {
CONSTRUCTED,
OPENED,
@@ -61,18 +66,20 @@ class WebContentsAudioInputStream::Impl
virtual ~Impl();
- // Returns true if the mirroring target has been permanently lost.
- bool IsTargetLost() const;
-
// Notifies the consumer callback that the stream is now dead.
void ReportError();
- // Start/Stop mirroring by posting a call to AudioMirroringManager on the IO
- // BrowserThread.
+ // (Re-)Start/Stop mirroring by posting a call to AudioMirroringManager on the
+ // IO BrowserThread.
void StartMirroring();
void StopMirroring();
// AudioMirroringManager::MirroringDestination implementation
+ virtual void QueryForMatches(
+ const std::set<SourceFrameRef>& candidates,
+ const MatchesCallback& results_callback) OVERRIDE;
+ void QueryForMatchesOnUIThread(const std::set<SourceFrameRef>& candidates,
+ const MatchesCallback& results_callback);
virtual media::AudioOutputStream* AddInput(
const media::AudioParameters& params) OVERRIDE;
@@ -81,7 +88,7 @@ class WebContentsAudioInputStream::Impl
// Called by WebContentsTracker when the target of the audio mirroring has
// changed.
- void OnTargetChanged(int render_process_id, int render_view_id);
+ void OnTargetChanged(RenderWidgetHost* target);
// Injected dependencies.
const int initial_render_process_id_;
@@ -94,10 +101,9 @@ class WebContentsAudioInputStream::Impl
State state_;
- // Current audio mirroring target.
- bool target_identified_;
- int target_render_process_id_;
- int target_render_view_id_;
+ // Set to true if |tracker_| reports a NULL target, which indicates the target
+ // is permanently lost.
+ bool is_target_lost_;
// Current callback used to consume the resulting mixed audio data.
AudioInputCallback* callback_;
@@ -118,9 +124,7 @@ WebContentsAudioInputStream::Impl::Impl(
tracker_(tracker),
mixer_stream_(mixer_stream),
state_(CONSTRUCTED),
- target_identified_(false),
- target_render_process_id_(-1),
- target_render_view_id_(-1),
+ is_target_lost_(false),
callback_(NULL) {
DCHECK(mirroring_manager_);
DCHECK(tracker_.get());
@@ -160,7 +164,7 @@ void WebContentsAudioInputStream::Impl::Start(AudioInputCallback* callback) {
return;
callback_ = callback;
- if (IsTargetLost()) {
+ if (is_target_lost_) {
ReportError();
callback_ = NULL;
return;
@@ -183,8 +187,7 @@ void WebContentsAudioInputStream::Impl::Stop() {
mixer_stream_->Stop();
callback_ = NULL;
- if (!IsTargetLost())
- StopMirroring();
+ StopMirroring();
}
void WebContentsAudioInputStream::Impl::Close() {
@@ -202,13 +205,6 @@ void WebContentsAudioInputStream::Impl::Close() {
state_ = CLOSED;
}
-bool WebContentsAudioInputStream::Impl::IsTargetLost() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!target_identified_)
- return false;
- return target_render_process_id_ <= 0 || target_render_view_id_ <= 0;
-}
-
void WebContentsAudioInputStream::Impl::ReportError() {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -225,7 +221,6 @@ void WebContentsAudioInputStream::Impl::StartMirroring() {
FROM_HERE,
base::Bind(&AudioMirroringManager::StartMirroring,
base::Unretained(mirroring_manager_),
- target_render_process_id_, target_render_view_id_,
make_scoped_refptr(this)));
}
@@ -237,10 +232,44 @@ void WebContentsAudioInputStream::Impl::StopMirroring() {
FROM_HERE,
base::Bind(&AudioMirroringManager::StopMirroring,
base::Unretained(mirroring_manager_),
- target_render_process_id_, target_render_view_id_,
make_scoped_refptr(this)));
}
+void WebContentsAudioInputStream::Impl::QueryForMatches(
+ const std::set<SourceFrameRef>& candidates,
+ const MatchesCallback& results_callback) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&Impl::QueryForMatchesOnUIThread,
+ this,
+ candidates,
+ media::BindToCurrentLoop(results_callback)));
+}
+
+void WebContentsAudioInputStream::Impl::QueryForMatchesOnUIThread(
+ const std::set<SourceFrameRef>& candidates,
+ const MatchesCallback& results_callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ std::set<SourceFrameRef> matches;
+ WebContents* const contents = tracker_->web_contents();
+ if (contents) {
+ // Add each ID to |matches| if it maps to a RenderFrameHost that maps to the
+ // currently-tracked WebContents.
+ for (std::set<SourceFrameRef>::const_iterator i = candidates.begin();
+ i != candidates.end(); ++i) {
+ WebContents* const contents_containing_frame =
+ WebContents::FromRenderFrameHost(
+ RenderFrameHost::FromID(i->first, i->second));
+ if (contents_containing_frame == contents)
+ matches.insert(*i);
+ }
+ }
+
+ results_callback.Run(matches);
+}
+
media::AudioOutputStream* WebContentsAudioInputStream::Impl::AddInput(
const media::AudioParameters& params) {
// Note: The closure created here holds a reference to "this," which will
@@ -257,29 +286,14 @@ void WebContentsAudioInputStream::Impl::ReleaseInput(
delete stream;
}
-void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id,
- int render_view_id) {
+void WebContentsAudioInputStream::Impl::OnTargetChanged(
+ RenderWidgetHost* target) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (target_identified_ &&
- target_render_process_id_ == render_process_id &&
- target_render_view_id_ == render_view_id) {
- return;
- }
-
- DVLOG(1) << "Target RenderView has changed from "
- << target_render_process_id_ << ':' << target_render_view_id_
- << " to " << render_process_id << ':' << render_view_id;
-
- if (state_ == MIRRORING)
- StopMirroring();
-
- target_identified_ = true;
- target_render_process_id_ = render_process_id;
- target_render_view_id_ = render_view_id;
+ is_target_lost_ = !target;
if (state_ == MIRRORING) {
- if (IsTargetLost()) {
+ if (is_target_lost_) {
ReportError();
Stop();
} else {
@@ -304,7 +318,7 @@ WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
return new WebContentsAudioInputStream(
render_process_id, main_render_frame_id,
audio_mirroring_manager,
- new WebContentsTracker(),
+ new WebContentsTracker(false),
new media::VirtualAudioInputStream(
params, worker_task_runner,
media::VirtualAudioInputStream::AfterCloseCallback()));
diff --git a/content/browser/media/capture/web_contents_audio_input_stream.h b/content/browser/media/capture/web_contents_audio_input_stream.h
index 9bdc914..b746500 100644
--- a/content/browser/media/capture/web_contents_audio_input_stream.h
+++ b/content/browser/media/capture/web_contents_audio_input_stream.h
@@ -3,15 +3,12 @@
// found in the LICENSE file.
//
// An AudioInputStream which provides a loop-back of all audio output generated
-// by the RenderView associated with a WebContents instance. The single stream
-// of data is produced by format-converting and mixing all audio output from a
-// RenderView. In other words, WebContentsAudioInputStream provides tab-level
+// by the entire RenderFrame tree associated with a WebContents instance. The
+// single stream of data is produced by format-converting and mixing all audio
+// output streams. As the RenderFrameHost tree mutates (e.g., due to page
+// navigations, or crashes/reloads), the stream will continue without
+// interruption. In other words, WebContentsAudioInputStream provides tab-level
// audio mirroring.
-//
-// The implementation observes a WebContents instance (which represents a
-// browser tab) so that it can track the replacement of RenderViews due to
-// navigation, crash/reload, etc. events; and take appropriate actions to
-// provide a seamless, uninterrupted mirroring experience.
#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_AUDIO_INPUT_STREAM_H_
#define CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_AUDIO_INPUT_STREAM_H_
diff --git a/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc b/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc
index 95592a8..7f0e319 100644
--- a/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc
+++ b/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc
@@ -41,9 +41,9 @@ namespace content {
namespace {
const int kRenderProcessId = 123;
-const int kRenderViewId = 456;
+const int kRenderFrameId = 456;
const int kAnotherRenderProcessId = 789;
-const int kAnotherRenderViewId = 1;
+const int kAnotherRenderFrameId = 1;
const AudioParameters& TestAudioParameters() {
static const AudioParameters params(
@@ -59,12 +59,8 @@ class MockAudioMirroringManager : public AudioMirroringManager {
MockAudioMirroringManager() : AudioMirroringManager() {}
virtual ~MockAudioMirroringManager() {}
- MOCK_METHOD3(StartMirroring,
- void(int render_process_id, int render_view_id,
- MirroringDestination* destination));
- MOCK_METHOD3(StopMirroring,
- void(int render_process_id, int render_view_id,
- MirroringDestination* destination));
+ MOCK_METHOD1(StartMirroring, void(MirroringDestination* destination));
+ MOCK_METHOD1(StopMirroring, void(MirroringDestination* destination));
private:
DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager);
@@ -72,10 +68,10 @@ class MockAudioMirroringManager : public AudioMirroringManager {
class MockWebContentsTracker : public WebContentsTracker {
public:
- MockWebContentsTracker() : WebContentsTracker() {}
+ MockWebContentsTracker() : WebContentsTracker(false) {}
MOCK_METHOD3(Start,
- void(int render_process_id, int render_view_id,
+ void(int render_process_id, int render_frame_id,
const ChangeCallback& callback));
MOCK_METHOD0(Stop, void());
@@ -187,7 +183,7 @@ class WebContentsAudioInputStreamTest : public testing::Test {
wcais_(NULL),
destination_(NULL),
current_render_process_id_(kRenderProcessId),
- current_render_view_id_(kRenderViewId),
+ current_render_frame_id_(kRenderFrameId),
on_data_event_(false, false) {
audio_thread_.Start();
}
@@ -210,16 +206,19 @@ class WebContentsAudioInputStreamTest : public testing::Test {
EXPECT_CALL(*mock_vais_, Close()); // At Close() time.
ASSERT_EQ(kRenderProcessId, current_render_process_id_);
- ASSERT_EQ(kRenderViewId, current_render_view_id_);
- EXPECT_CALL(*mock_tracker_.get(), Start(kRenderProcessId, kRenderViewId, _))
+ ASSERT_EQ(kRenderFrameId, current_render_frame_id_);
+ EXPECT_CALL(*mock_tracker_.get(),
+ Start(kRenderProcessId, kRenderFrameId, _))
.WillOnce(DoAll(
SaveArg<2>(&change_callback_),
- WithArgs<0, 1>(Invoke(&change_callback_,
- &WebContentsTracker::ChangeCallback::Run))));
+ WithArgs<0, 1>(Invoke(this,
+ &WebContentsAudioInputStreamTest::
+ SimulateChangeCallback))));
+
EXPECT_CALL(*mock_tracker_.get(), Stop()); // At Close() time.
wcais_ = new WebContentsAudioInputStream(
- current_render_process_id_, current_render_view_id_,
+ current_render_process_id_, current_render_frame_id_,
mock_mirroring_manager_.get(),
mock_tracker_, mock_vais_);
wcais_->Open();
@@ -229,13 +228,11 @@ class WebContentsAudioInputStreamTest : public testing::Test {
EXPECT_CALL(*mock_vais_, Start(&mock_input_callback_));
EXPECT_CALL(*mock_vais_, Stop()); // At Stop() time.
- EXPECT_CALL(*mock_mirroring_manager_,
- StartMirroring(kRenderProcessId, kRenderViewId, NotNull()))
- .WillOnce(SaveArg<2>(&destination_))
+ EXPECT_CALL(*mock_mirroring_manager_, StartMirroring(NotNull()))
+ .WillOnce(SaveArg<0>(&destination_))
.RetiresOnSaturation();
// At Stop() time, or when the mirroring target changes:
- EXPECT_CALL(*mock_mirroring_manager_,
- StopMirroring(kRenderProcessId, kRenderViewId, NotNull()))
+ EXPECT_CALL(*mock_mirroring_manager_, StopMirroring(NotNull()))
.WillOnce(Assign(
&destination_,
static_cast<AudioMirroringManager::MirroringDestination*>(NULL)))
@@ -309,38 +306,24 @@ class WebContentsAudioInputStreamTest : public testing::Test {
const int next_render_process_id =
current_render_process_id_ == kRenderProcessId ?
kAnotherRenderProcessId : kRenderProcessId;
- const int next_render_view_id =
- current_render_view_id_ == kRenderViewId ?
- kAnotherRenderViewId : kRenderViewId;
-
- EXPECT_CALL(*mock_mirroring_manager_,
- StartMirroring(next_render_process_id, next_render_view_id,
- NotNull()))
- .WillOnce(SaveArg<2>(&destination_))
- .RetiresOnSaturation();
- // At Stop() time, or when the mirroring target changes:
- EXPECT_CALL(*mock_mirroring_manager_,
- StopMirroring(next_render_process_id, next_render_view_id,
- NotNull()))
- .WillOnce(Assign(
- &destination_,
- static_cast<AudioMirroringManager::MirroringDestination*>(NULL)))
+ const int next_render_frame_id =
+ current_render_frame_id_ == kRenderFrameId ?
+ kAnotherRenderFrameId : kRenderFrameId;
+
+ EXPECT_CALL(*mock_mirroring_manager_, StartMirroring(NotNull()))
+ .WillOnce(SaveArg<0>(&destination_))
.RetiresOnSaturation();
- // Simulate OnTargetChange() callback from WebContentsTracker.
- EXPECT_FALSE(change_callback_.is_null());
- change_callback_.Run(next_render_process_id, next_render_view_id);
+ SimulateChangeCallback(next_render_process_id, next_render_frame_id);
current_render_process_id_ = next_render_process_id;
- current_render_view_id_ = next_render_view_id;
+ current_render_frame_id_ = next_render_frame_id;
}
void LoseMirroringTarget() {
EXPECT_CALL(mock_input_callback_, OnError(_));
- // Simulate OnTargetChange() callback from WebContentsTracker.
- EXPECT_FALSE(change_callback_.is_null());
- change_callback_.Run(-1, -1);
+ SimulateChangeCallback(-1, -1);
}
void Stop() {
@@ -370,6 +353,17 @@ class WebContentsAudioInputStreamTest : public testing::Test {
}
private:
+ void SimulateChangeCallback(int render_process_id, int render_frame_id) {
+ ASSERT_FALSE(change_callback_.is_null());
+ if (render_process_id == -1 || render_frame_id == -1) {
+ change_callback_.Run(NULL);
+ } else {
+ // For our tests, any non-NULL value will suffice since it will not be
+ // dereferenced.
+ change_callback_.Run(reinterpret_cast<RenderWidgetHost*>(0xdeadbee5));
+ }
+ }
+
scoped_ptr<TestBrowserThreadBundle> thread_bundle_;
base::Thread audio_thread_;
@@ -392,9 +386,9 @@ class WebContentsAudioInputStreamTest : public testing::Test {
// to simulate: 1) calls to AddInput(); and 2) diverting audio data.
AudioMirroringManager::MirroringDestination* destination_;
- // Current target RenderView. These get flipped in ChangedMirroringTarget().
+ // Current target RenderFrame. These get flipped in ChangedMirroringTarget().
int current_render_process_id_;
- int current_render_view_id_;
+ int current_render_frame_id_;
// Streams provided by calls to WebContentsAudioInputStream::AddInput(). Each
// is started with a simulated source of audio data.
diff --git a/content/browser/media/capture/web_contents_tracker.cc b/content/browser/media/capture/web_contents_tracker.cc
index 5bfdf4e..6810ac0 100644
--- a/content/browser/media/capture/web_contents_tracker.cc
+++ b/content/browser/media/capture/web_contents_tracker.cc
@@ -5,15 +5,18 @@
#include "content/browser/media/capture/web_contents_tracker.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
namespace content {
-WebContentsTracker::WebContentsTracker() {}
+WebContentsTracker::WebContentsTracker(bool track_fullscreen_rwh)
+ : track_fullscreen_rwh_(track_fullscreen_rwh),
+ last_target_(NULL) {}
WebContentsTracker::~WebContentsTracker() {
DCHECK(!web_contents()) << "BUG: Still observering!";
@@ -27,10 +30,14 @@ void WebContentsTracker::Start(int render_process_id, int main_render_frame_id,
DCHECK(message_loop_.get());
callback_ = callback;
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&WebContentsTracker::LookUpAndObserveWebContents, this,
- render_process_id, main_render_frame_id));
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ StartObservingWebContents(render_process_id, main_render_frame_id);
+ } else {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&WebContentsTracker::StartObservingWebContents, this,
+ render_process_id, main_render_frame_id));
+ }
}
void WebContentsTracker::Stop() {
@@ -38,38 +45,68 @@ void WebContentsTracker::Stop() {
callback_.Reset();
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&WebContentsTracker::Observe, this,
- static_cast<WebContents*>(NULL)));
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ WebContentsObserver::Observe(NULL);
+ } else {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&WebContentsTracker::Observe, this,
+ static_cast<WebContents*>(NULL)));
+ }
}
-void WebContentsTracker::OnWebContentsChangeEvent() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+RenderWidgetHost* WebContentsTracker::GetTargetRenderWidgetHost() const {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
WebContents* const wc = web_contents();
- RenderViewHost* const rvh = wc ? wc->GetRenderViewHost() : NULL;
- RenderProcessHost* const rph = rvh ? rvh->GetProcess() : NULL;
-
- const int render_process_id = rph ? rph->GetID() : MSG_ROUTING_NONE;
- const int render_view_id = rvh ? rvh->GetRoutingID() : MSG_ROUTING_NONE;
+ if (!wc)
+ return NULL;
+
+ RenderWidgetHost* rwh = NULL;
+ if (track_fullscreen_rwh_) {
+ RenderWidgetHostView* const view = wc->GetFullscreenRenderWidgetHostView();
+ if (view)
+ rwh = view->GetRenderWidgetHost();
+ }
+ if (!rwh) {
+ RenderFrameHostImpl* const rfh =
+ static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
+ if (rfh)
+ rwh = rfh->GetRenderWidgetHost();
+ }
+
+ return rwh;
+}
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&WebContentsTracker::MaybeDoCallback, this,
- render_process_id, render_view_id));
+void WebContentsTracker::OnPossibleTargetChange(bool force_callback_run) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ RenderWidgetHost* const rwh = GetTargetRenderWidgetHost();
+ if (rwh == last_target_ && !force_callback_run)
+ return;
+ DVLOG(1) << "Will report target change from RenderWidgetHost@" << last_target_
+ << " to RenderWidgetHost@" << rwh;
+ last_target_ = rwh;
+
+ if (message_loop_->BelongsToCurrentThread()) {
+ MaybeDoCallback(rwh);
+ } else {
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&WebContentsTracker::MaybeDoCallback, this, rwh));
+ }
}
-void WebContentsTracker::MaybeDoCallback(int render_process_id,
- int render_view_id) {
+void WebContentsTracker::MaybeDoCallback(RenderWidgetHost* rwh) {
DCHECK(message_loop_->BelongsToCurrentThread());
if (!callback_.is_null())
- callback_.Run(render_process_id, render_view_id);
+ callback_.Run(rwh);
}
-void WebContentsTracker::LookUpAndObserveWebContents(int render_process_id,
- int main_render_frame_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+void WebContentsTracker::StartObservingWebContents(int render_process_id,
+ int main_render_frame_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
Observe(WebContents::FromRenderFrameHost(RenderFrameHost::FromID(
render_process_id, main_render_frame_id)));
@@ -78,24 +115,30 @@ void WebContentsTracker::LookUpAndObserveWebContents(int render_process_id,
<< "referenced by render_process_id=" << render_process_id
<< ", routing_id=" << main_render_frame_id;
- OnWebContentsChangeEvent();
+ OnPossibleTargetChange(true);
+}
+
+void WebContentsTracker::RenderFrameDeleted(
+ RenderFrameHost* render_frame_host) {
+ OnPossibleTargetChange(false);
}
-void WebContentsTracker::RenderViewReady() {
- OnWebContentsChangeEvent();
+void WebContentsTracker::RenderFrameHostChanged(RenderFrameHost* old_host,
+ RenderFrameHost* new_host) {
+ OnPossibleTargetChange(false);
}
-void WebContentsTracker::AboutToNavigateRenderView(RenderViewHost* rvh) {
- OnWebContentsChangeEvent();
+void WebContentsTracker::WebContentsDestroyed() {
+ Observe(NULL);
+ OnPossibleTargetChange(false);
}
-void WebContentsTracker::DidNavigateMainFrame(
- const LoadCommittedDetails& details, const FrameNavigateParams& params) {
- OnWebContentsChangeEvent();
+void WebContentsTracker::DidShowFullscreenWidget(int routing_id) {
+ OnPossibleTargetChange(false);
}
-void WebContentsTracker::WebContentsDestroyed() {
- OnWebContentsChangeEvent();
+void WebContentsTracker::DidDestroyFullscreenWidget(int routing_id) {
+ OnPossibleTargetChange(false);
}
} // namespace content
diff --git a/content/browser/media/capture/web_contents_tracker.h b/content/browser/media/capture/web_contents_tracker.h
index bd551ca..47d7e18 100644
--- a/content/browser/media/capture/web_contents_tracker.h
+++ b/content/browser/media/capture/web_contents_tracker.h
@@ -3,14 +3,11 @@
// found in the LICENSE file.
//
// Given a starting render_process_id and main_render_frame_id, the
-// WebContentsTracker tracks RenderViewHost instance swapping during the
-// lifetime of a WebContents instance. This is used when mirroring tab video
-// and audio so that user navigations, crashes, etc., during a tab's lifetime
-// allow the capturing code to remain active on the current/latest RenderView.
-//
-// TODO(miu): In a soon upcoming change, the cross-site isolation migration of
-// this code will be completed such that the main RenderFrameHost is tracked
-// instead of the RenderViewHost.
+// WebContentsTracker tracks changes to the active RenderFrameHost tree during
+// the lifetime of a WebContents instance. This is used when mirroring tab
+// video and audio so that user navigations, crashes, iframes, etc., during a
+// tab's lifetime allow the capturing code to remain active on the
+// current/latest render frame tree.
//
// Threading issues: Start(), Stop() and the ChangeCallback are invoked on the
// same thread. This can be any thread, and the decision is locked-in by
@@ -30,22 +27,27 @@ class MessageLoopProxy;
namespace content {
+class RenderWidgetHost;
+
class CONTENT_EXPORT WebContentsTracker
: public base::RefCountedThreadSafe<WebContentsTracker>,
public WebContentsObserver {
public:
- WebContentsTracker();
+ // If |track_fullscreen_rwh| is true, the ChangeCallback will be run when a
+ // WebContents shows/destroys a fullscreen RenderWidgetHost view. If false,
+ // fullscreen events are ignored. Specify true for video tab capture and
+ // false for audio tab capture.
+ explicit WebContentsTracker(bool track_fullscreen_rwh);
- // Callback for whenever the target is swapped. The callback is also invoked
- // with both arguments set to MSG_ROUTING_NONE to indicate tracking will not
- // continue (i.e., the WebContents instance was not found or has been
- // destroyed).
- typedef base::Callback<void(int render_process_id, int render_view_id)>
- ChangeCallback;
+ // Callback to indicate a new RenderWidgetHost should be targeted for capture.
+ // This is also invoked with NULL to indicate tracking will not continue
+ // (i.e., the WebContents instance was not found or has been destroyed).
+ typedef base::Callback<void(RenderWidgetHost* rwh)> ChangeCallback;
// Start tracking. The last-known |render_process_id| and
- // |main_render_frame_id| are provided, and the given callback is invoked
- // asynchronously one or more times. The callback will be invoked on the same
+ // |main_render_frame_id| are provided, and |callback| will be run once to
+ // indicate the current capture target (this may occur during the invocation
+ // of Start(), or in the future). The callback will be invoked on the same
// thread calling Start().
virtual void Start(int render_process_id, int main_render_frame_id,
const ChangeCallback& callback);
@@ -54,35 +56,59 @@ class CONTENT_EXPORT WebContentsTracker
// be invoked again.
virtual void Stop();
+ // Current target. This must only be called on the UI BrowserThread.
+ RenderWidgetHost* GetTargetRenderWidgetHost() const;
+
protected:
friend class base::RefCountedThreadSafe<WebContentsTracker>;
virtual ~WebContentsTracker();
private:
- // Reads the render_process_id/render_view_id from the current WebContents
- // instance and then invokes the callback.
- void OnWebContentsChangeEvent();
+ // Determine the target RenderWidgetHost and, if different from that last
+ // reported, runs the ChangeCallback on the appropriate thread. If
+ // |force_callback_run| is true, the ChangeCallback is run even if the
+ // RenderWidgetHost has not changed.
+ void OnPossibleTargetChange(bool force_callback_run);
- // Called on the thread that Start()/Stop() are called on, check whether the
- // callback is still valid and, if so, invoke it.
- void MaybeDoCallback(int render_process_id, int render_view_id);
+ // Called on the thread that Start()/Stop() are called on. Checks whether the
+ // callback is still valid and, if so, runs it.
+ void MaybeDoCallback(RenderWidgetHost* rwh);
// Look-up the current WebContents instance associated with the given
// |render_process_id| and |main_render_frame_id| and begin observing it.
- void LookUpAndObserveWebContents(int render_process_id,
- int main_render_frame_id);
-
- // WebContentsObserver overrides to react to events of interest.
- virtual void RenderViewReady() OVERRIDE;
- virtual void AboutToNavigateRenderView(RenderViewHost* render_view_host)
- OVERRIDE;
- virtual void DidNavigateMainFrame(const LoadCommittedDetails& details,
- const FrameNavigateParams& params) OVERRIDE;
+ void StartObservingWebContents(int render_process_id,
+ int main_render_frame_id);
+
+ // WebContentsObserver overrides: According to web_contents_observer.h, these
+ // two method overrides are all that is necessary to track the set of active
+ // RenderFrameHosts.
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual void RenderFrameHostChanged(RenderFrameHost* old_host,
+ RenderFrameHost* new_host) OVERRIDE;
+
+ // WebContentsObserver override to notify the client that the capture target
+ // has been permanently lost.
virtual void WebContentsDestroyed() OVERRIDE;
+ // WebContentsObserver overrides to notify the client that the capture target
+ // may have changed due to a separate fullscreen widget shown/destroyed.
+ virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE;
+ virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE;
+
+ // If true, the client is interested in the showing/destruction of fullscreen
+ // RenderWidgetHosts.
+ const bool track_fullscreen_rwh_;
+
+ // MessageLoop corresponding to the thread that called Start().
scoped_refptr<base::MessageLoopProxy> message_loop_;
+
+ // Callback to run when the target RenderWidgetHost has changed.
ChangeCallback callback_;
+ // Pointer to the RenderWidgetHost provided in the last run of |callback_|.
+ // This is used to eliminate duplicate callback runs.
+ RenderWidgetHost* last_target_;
+
DISALLOW_COPY_AND_ASSIGN(WebContentsTracker);
};
diff --git a/content/browser/media/capture/web_contents_video_capture_device.cc b/content/browser/media/capture/web_contents_video_capture_device.cc
index 654b8dc..3bd7906 100644
--- a/content/browser/media/capture/web_contents_video_capture_device.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device.cc
@@ -13,7 +13,7 @@
// video encoder -- is the performance bottleneck, and that the rate of
// frame capture should be throttled back.
//
-// 2. Capture: A bitmap is snapshotted/copied from the RenderView's backing
+// 2. Capture: A bitmap is snapshotted/copied from the RenderWidget's backing
// store. This is initiated on the UI BrowserThread, and often occurs
// asynchronously. Where supported, the GPU scales and color converts
// frames to our desired size, and the readback happens directly into the
@@ -65,16 +65,18 @@
#include "content/browser/media/capture/content_video_capture_device_core.h"
#include "content/browser/media/capture/video_capture_oracle.h"
#include "content/browser/media/capture/web_contents_capture_util.h"
+#include "content/browser/media/capture/web_contents_tracker.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
-#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
-#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
-#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents.h"
#include "media/base/video_util.h"
#include "media/video/capture/video_capture_types.h"
#include "skia/ext/image_operations.h"
@@ -193,8 +195,11 @@ class ContentCaptureSubscription : public content::NotificationObserver {
private:
void OnTimer();
+ // Maintain a weak reference to the RenderWidgetHost (via its routing ID),
+ // since the instance could be destroyed externally during the lifetime of
+ // |this|.
const int render_process_id_;
- const int render_view_id_;
+ const int render_widget_id_;
VideoFrameDeliveryLog delivery_log_;
FrameSubscriber paint_subscriber_;
@@ -219,16 +224,9 @@ void RenderVideoFrame(const SkBitmap& input,
const scoped_refptr<media::VideoFrame>& output,
const base::Callback<void(bool)>& done_cb);
-// Keeps track of the RenderView to be sourced, and executes copying of the
-// backing store on the UI BrowserThread.
-//
-// TODO(nick): It would be nice to merge this with WebContentsTracker, but its
-// implementation is currently asynchronous -- in our case, the "rvh changed"
-// notification would get posted back to the UI thread and processed later, and
-// this seems disadvantageous.
-class WebContentsCaptureMachine
- : public VideoCaptureMachine,
- public WebContentsObserver {
+// Renews capture subscriptions based on feedback from WebContentsTracker, and
+// also executes copying of the backing store on the UI BrowserThread.
+class WebContentsCaptureMachine : public VideoCaptureMachine {
public:
WebContentsCaptureMachine(int render_process_id, int main_render_frame_id);
virtual ~WebContentsCaptureMachine();
@@ -248,44 +246,12 @@ class WebContentsCaptureMachine
const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
deliver_frame_cb);
- // content::WebContentsObserver implementation.
- virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE {
- fullscreen_widget_id_ = routing_id;
- RenewFrameSubscription();
- }
-
- virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE {
- DCHECK_EQ(fullscreen_widget_id_, routing_id);
- fullscreen_widget_id_ = MSG_ROUTING_NONE;
- RenewFrameSubscription();
- }
-
- virtual void RenderViewReady() OVERRIDE {
- RenewFrameSubscription();
- }
-
- virtual void AboutToNavigateRenderView(RenderViewHost* rvh) OVERRIDE {
- RenewFrameSubscription();
- }
-
- virtual void DidNavigateMainFrame(
- const LoadCommittedDetails& details,
- const FrameNavigateParams& params) OVERRIDE {
- RenewFrameSubscription();
- }
-
- virtual void WebContentsDestroyed() OVERRIDE;
-
private:
+ bool IsStarted() const;
+
// Computes the preferred size of the target RenderWidget for optimal capture.
gfx::Size ComputeOptimalTargetSize() const;
- // Starts observing the web contents, returning false if lookup fails.
- bool StartObservingWebContents();
-
- // Helper function to determine the view that we are currently tracking.
- RenderWidgetHost* GetTarget() const;
-
// Response callback for RenderWidgetHost::CopyFromBackingStore().
void DidCopyFromBackingStore(
const base::TimeTicks& start_time,
@@ -302,15 +268,17 @@ class WebContentsCaptureMachine
deliver_frame_cb,
bool success);
- // Remove the old subscription, and start a new one. This should be called
- // after any change to the WebContents that affects the RenderWidgetHost or
- // attached views.
- void RenewFrameSubscription();
+ // Remove the old subscription, and start a new one if |rwh| is not NULL.
+ void RenewFrameSubscription(RenderWidgetHost* rwh);
// Parameters saved in constructor.
const int initial_render_process_id_;
const int initial_main_render_frame_id_;
+ // Tracks events and calls back to RenewFrameSubscription() to maintain
+ // capture on the correct RenderWidgetHost.
+ const scoped_refptr<WebContentsTracker> tracker_;
+
// A dedicated worker thread on which SkBitmap->VideoFrame conversion will
// occur. Only used when this activity cannot be done on the GPU.
scoped_ptr<base::Thread> render_thread_;
@@ -321,10 +289,6 @@ class WebContentsCaptureMachine
// Video capture parameters that this machine is started with.
media::VideoCaptureParams capture_params_;
- // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE
- // otherwise.
- int fullscreen_widget_id_;
-
// Last known RenderView size.
gfx::Size last_view_size_;
@@ -363,7 +327,7 @@ ContentCaptureSubscription::ContentCaptureSubscription(
const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
const CaptureCallback& capture_callback)
: render_process_id_(source.GetProcess()->GetID()),
- render_view_id_(source.GetRoutingID()),
+ render_widget_id_(source.GetRoutingID()),
delivery_log_(),
paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy,
&delivery_log_),
@@ -373,8 +337,7 @@ ContentCaptureSubscription::ContentCaptureSubscription(
timer_(true, true) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
- source.GetView());
+ RenderWidgetHostView* const view = source.GetView();
// Subscribe to accelerated presents. These will be serviced directly by the
// oracle.
@@ -407,14 +370,11 @@ ContentCaptureSubscription::~ContentCaptureSubscription() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (kAcceleratedSubscriberIsSupported) {
- RenderViewHost* source = RenderViewHost::FromID(render_process_id_,
- render_view_id_);
- if (source) {
- RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
- source->GetView());
- if (view)
- view->EndFrameSubscription();
- }
+ RenderWidgetHost* const source =
+ RenderWidgetHost::FromID(render_process_id_, render_widget_id_);
+ RenderWidgetHostView* const view = source ? source->GetView() : NULL;
+ if (view)
+ view->EndFrameSubscription();
}
}
@@ -578,16 +538,21 @@ WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id,
int main_render_frame_id)
: initial_render_process_id_(render_process_id),
initial_main_render_frame_id_(main_render_frame_id),
- fullscreen_widget_id_(MSG_ROUTING_NONE),
+ tracker_(new WebContentsTracker(true)),
weak_ptr_factory_(this) {}
WebContentsCaptureMachine::~WebContentsCaptureMachine() {}
+bool WebContentsCaptureMachine::IsStarted() const {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ return weak_ptr_factory_.HasWeakPtrs();
+}
+
bool WebContentsCaptureMachine::Start(
const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
const media::VideoCaptureParams& params) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- DCHECK(!weak_ptr_factory_.HasWeakPtrs()); // Should not be started.
+ DCHECK(!IsStarted());
DCHECK(oracle_proxy.get());
oracle_proxy_ = oracle_proxy;
@@ -600,27 +565,32 @@ bool WebContentsCaptureMachine::Start(
return false;
}
- if (!StartObservingWebContents()) {
- DVLOG(1) << "Failed to observe web contents.";
- render_thread_.reset();
- return false;
- }
+ // Note: Creation of the first WeakPtr in the following statement will cause
+ // IsStarted() to return true from now on.
+ tracker_->Start(initial_render_process_id_, initial_main_render_frame_id_,
+ base::Bind(&WebContentsCaptureMachine::RenewFrameSubscription,
+ weak_ptr_factory_.GetWeakPtr()));
return true;
}
void WebContentsCaptureMachine::Stop(const base::Closure& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- subscription_.reset();
- if (web_contents()) {
- web_contents()->DecrementCapturerCount();
- Observe(NULL);
+
+ if (!IsStarted()) {
+ callback.Run();
+ return;
}
- // Any callback that intend to use render_thread_ will not work after it is
- // passed.
+ // The following cancels any outstanding callbacks and causes IsStarted() to
+ // return false from here onward.
weak_ptr_factory_.InvalidateWeakPtrs();
+ // Note: RenewFrameSubscription() must be called before stopping |tracker_| so
+ // the web_contents() can be notified that the capturing is ending.
+ RenewFrameSubscription(NULL);
+ tracker_->Stop();
+
// The render thread cannot be stopped on the UI thread, so post a message
// to the thread pool used for blocking operations.
if (render_thread_.get()) {
@@ -638,10 +608,10 @@ void WebContentsCaptureMachine::Capture(
deliver_frame_cb) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- RenderWidgetHost* rwh = GetTarget();
+ RenderWidgetHost* rwh = tracker_->GetTargetRenderWidgetHost();
RenderWidgetHostViewBase* view =
rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL;
- if (!view || !rwh) {
+ if (!view) {
deliver_frame_cb.Run(base::TimeTicks(), false);
return;
}
@@ -692,7 +662,7 @@ gfx::Size WebContentsCaptureMachine::ComputeOptimalTargetSize() const {
// render widget to the "preferred size," the widget will be physically
// rendered at the exact capture size, thereby eliminating unnecessary scaling
// operations in the graphics pipeline.
- RenderWidgetHost* const rwh = GetTarget();
+ RenderWidgetHost* const rwh = tracker_->GetTargetRenderWidgetHost();
RenderWidgetHostView* const rwhv = rwh ? rwh->GetView() : NULL;
if (rwhv) {
const gfx::NativeView view = rwhv->GetNativeView();
@@ -713,60 +683,6 @@ gfx::Size WebContentsCaptureMachine::ComputeOptimalTargetSize() const {
return optimal_size;
}
-bool WebContentsCaptureMachine::StartObservingWebContents() {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
- // Look-up the RenderFrameHost and, from that, the WebContents that wraps it.
- // If successful, begin observing the WebContents instance.
- //
- // Why this can be unsuccessful: The request for mirroring originates in a
- // render process, and this request is based on the current main RenderFrame
- // associated with a tab. However, by the time we get up-and-running here,
- // there have been multiple back-and-forth IPCs between processes, as well as
- // a bit of indirection across threads. It's easily possible that, in the
- // meantime, the original RenderFrame may have gone away.
- Observe(WebContents::FromRenderFrameHost(RenderFrameHost::FromID(
- initial_render_process_id_, initial_main_render_frame_id_)));
- DVLOG_IF(1, !web_contents())
- << "Could not find WebContents associated with main RenderFrameHost "
- << "referenced by render_process_id=" << initial_render_process_id_
- << ", routing_id=" << initial_main_render_frame_id_;
-
- WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
- if (contents) {
- contents->IncrementCapturerCount(ComputeOptimalTargetSize());
- fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID();
- RenewFrameSubscription();
- return true;
- }
- return false;
-}
-
-void WebContentsCaptureMachine::WebContentsDestroyed() {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
- subscription_.reset();
- web_contents()->DecrementCapturerCount();
- oracle_proxy_->ReportError("WebContentsDestroyed()");
-}
-
-RenderWidgetHost* WebContentsCaptureMachine::GetTarget() const {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (!web_contents())
- return NULL;
-
- RenderWidgetHost* rwh = NULL;
- if (fullscreen_widget_id_ != MSG_ROUTING_NONE) {
- RenderProcessHost* process = web_contents()->GetRenderProcessHost();
- if (process)
- rwh = RenderWidgetHost::FromID(process->GetID(), fullscreen_widget_id_);
- } else {
- rwh = web_contents()->GetRenderViewHost();
- }
-
- return rwh;
-}
-
void WebContentsCaptureMachine::DidCopyFromBackingStore(
const base::TimeTicks& start_time,
const scoped_refptr<media::VideoFrame>& target,
@@ -809,15 +725,31 @@ void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
deliver_frame_cb.Run(start_time, success);
}
-void WebContentsCaptureMachine::RenewFrameSubscription() {
+void WebContentsCaptureMachine::RenewFrameSubscription(RenderWidgetHost* rwh) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Always destroy the old subscription before creating a new one.
+ const bool had_subscription = !!subscription_;
subscription_.reset();
- RenderWidgetHost* rwh = GetTarget();
- if (!rwh || !rwh->GetView())
+ DVLOG(1) << "Renewing frame subscription to RWH@" << rwh
+ << ", had_subscription=" << had_subscription;
+
+ if (!rwh) {
+ if (had_subscription && tracker_->web_contents())
+ tracker_->web_contents()->DecrementCapturerCount();
+ if (IsStarted()) {
+ // Tracking of WebContents and/or its main frame has failed before Stop()
+ // was called, so report this as an error:
+ oracle_proxy_->ReportError("WebContents and/or main frame are gone.");
+ }
return;
+ }
+
+ if (!had_subscription && tracker_->web_contents()) {
+ tracker_->web_contents()->IncrementCapturerCount(
+ ComputeOptimalTargetSize());
+ }
subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_,
base::Bind(&WebContentsCaptureMachine::Capture,
diff --git a/content/browser/media/capture/web_contents_video_capture_device.h b/content/browser/media/capture/web_contents_video_capture_device.h
index e7d31f6..1ec998c 100644
--- a/content/browser/media/capture/web_contents_video_capture_device.h
+++ b/content/browser/media/capture/web_contents_video_capture_device.h
@@ -16,19 +16,15 @@ namespace content {
class ContentVideoCaptureDeviceCore;
// A virtualized VideoCaptureDevice that mirrors the displayed contents of a
-// tab (accessed via its associated WebContents instance), producing a stream of
-// video frames.
+// WebContents (i.e., the composition of an entire render frame tree), producing
+// a stream of video frames.
//
// An instance is created by providing a device_id. The device_id contains
// information necessary for finding a WebContents instance. From then on,
-// WebContentsVideoCaptureDevice will capture from whatever render view is
-// currently associated with that WebContents instance. This allows the
-// underlying render view to be swapped out (e.g., due to navigation or
-// crashes/reloads), without any interruption in capturing.
-//
-// TODO(miu): In a soon upcoming change, the cross-site isolation migration of
-// this code will be completed such that the main RenderFrameHost is tracked
-// instead of the RenderViewHost.
+// WebContentsVideoCaptureDevice will capture from the RenderWidgetHost that
+// encompasses the currently active RenderFrameHost tree for that that
+// WebContents instance. As the RenderFrameHost tree mutates (e.g., due to page
+// navigations, or crashes/reloads), capture will continue without interruption.
class CONTENT_EXPORT WebContentsVideoCaptureDevice
: public media::VideoCaptureDevice {
public:
diff --git a/content/browser/renderer_host/media/audio_renderer_host.cc b/content/browser/renderer_host/media/audio_renderer_host.cc
index f93eb41..a34bb18 100644
--- a/content/browser/renderer_host/media/audio_renderer_host.cc
+++ b/content/browser/renderer_host/media/audio_renderer_host.cc
@@ -380,7 +380,7 @@ void AudioRendererHost::OnCreateStream(
reader.PassAs<media::AudioOutputController::SyncReader>()));
if (mirroring_manager_) {
mirroring_manager_->AddDiverter(
- render_process_id_, entry->render_view_id(), entry->controller());
+ render_process_id_, entry->render_frame_id(), entry->controller());
}
audio_entries_.insert(std::make_pair(stream_id, entry.release()));
audio_log_->OnCreated(stream_id, params, output_device_id);
@@ -445,10 +445,8 @@ void AudioRendererHost::OnCloseStream(int stream_id) {
audio_entries_.erase(i);
media::AudioOutputController* const controller = entry->controller();
- if (mirroring_manager_) {
- mirroring_manager_->RemoveDiverter(
- render_process_id_, entry->render_view_id(), controller);
- }
+ if (mirroring_manager_)
+ mirroring_manager_->RemoveDiverter(controller);
controller->Close(
base::Bind(&AudioRendererHost::DeleteEntry, this, base::Passed(&entry)));
audio_log_->OnClosed(stream_id);
diff --git a/content/browser/renderer_host/media/audio_renderer_host_unittest.cc b/content/browser/renderer_host/media/audio_renderer_host_unittest.cc
index 286e1e7..8710b0d 100644
--- a/content/browser/renderer_host/media/audio_renderer_host_unittest.cc
+++ b/content/browser/renderer_host/media/audio_renderer_host_unittest.cc
@@ -43,12 +43,9 @@ class MockAudioMirroringManager : public AudioMirroringManager {
MOCK_METHOD3(AddDiverter,
void(int render_process_id,
- int render_view_id,
- Diverter* diverter));
- MOCK_METHOD3(RemoveDiverter,
- void(int render_process_id,
- int render_view_id,
+ int render_frame_id,
Diverter* diverter));
+ MOCK_METHOD1(RemoveDiverter, void(Diverter* diverter));
private:
DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager);
@@ -176,7 +173,7 @@ class AudioRendererHostTest : public testing::Test {
EXPECT_CALL(*host_.get(), OnStreamCreated(kStreamId, _));
EXPECT_CALL(mirroring_manager_,
- AddDiverter(kRenderProcessId, kRenderViewId, NotNull()))
+ AddDiverter(kRenderProcessId, kRenderFrameId, NotNull()))
.RetiresOnSaturation();
// Send a create stream message to the audio output stream and wait until
@@ -206,8 +203,7 @@ class AudioRendererHostTest : public testing::Test {
// At some point in the future, a corresponding RemoveDiverter() call must
// be made.
- EXPECT_CALL(mirroring_manager_,
- RemoveDiverter(kRenderProcessId, kRenderViewId, NotNull()))
+ EXPECT_CALL(mirroring_manager_, RemoveDiverter(NotNull()))
.RetiresOnSaturation();
SyncWithAudioThread();
}