Home | History | Annotate | Download | only in media
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/media/webrtc_internals.h"
      6 
      7 #include "base/path_service.h"
      8 #include "content/browser/media/webrtc_internals_ui_observer.h"
      9 #include "content/browser/web_contents/web_contents_view.h"
     10 #include "content/public/browser/browser_thread.h"
     11 #include "content/public/browser/content_browser_client.h"
     12 #include "content/public/browser/notification_service.h"
     13 #include "content/public/browser/notification_types.h"
     14 #include "content/public/browser/render_process_host.h"
     15 #include "content/public/browser/web_contents.h"
     16 
     17 using base::ProcessId;
     18 using std::string;
     19 
     20 namespace content {
     21 
     22 namespace {
     23 // Makes sure that |dict| has a ListValue under path "log".
     24 static base::ListValue* EnsureLogList(base::DictionaryValue* dict) {
     25   base::ListValue* log = NULL;
     26   if (!dict->GetList("log", &log)) {
     27     log = new base::ListValue();
     28     if (log)
     29       dict->Set("log", log);
     30   }
     31   return log;
     32 }
     33 
     34 }  // namespace
     35 
     36 WebRTCInternals::WebRTCInternals()
     37     : aec_dump_enabled_(false) {
     38   registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
     39                  NotificationService::AllBrowserContextsAndSources());
     40 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
     41 // build if WebRTC is disabled?
     42 #if defined(ENABLE_WEBRTC)
     43   aec_dump_file_path_ =
     44       GetContentClient()->browser()->GetDefaultDownloadDirectory();
     45   if (aec_dump_file_path_.empty()) {
     46     // In this case the default path (|aec_dump_file_path_|) will be empty and
     47     // the platform default path will be used in the file dialog (with no
     48     // default file name). See SelectFileDialog::SelectFile. On Android where
     49     // there's no dialog we'll fail to open the file.
     50     VLOG(1) << "Could not get the download directory.";
     51   } else {
     52     aec_dump_file_path_ =
     53         aec_dump_file_path_.Append(FILE_PATH_LITERAL("audio.aecdump"));
     54   }
     55 #endif  // defined(ENABLE_WEBRTC)
     56 }
     57 
     58 WebRTCInternals::~WebRTCInternals() {
     59 }
     60 
     61 WebRTCInternals* WebRTCInternals::GetInstance() {
     62   return Singleton<WebRTCInternals>::get();
     63 }
     64 
     65 void WebRTCInternals::OnAddPeerConnection(int render_process_id,
     66                                           ProcessId pid,
     67                                           int lid,
     68                                           const string& url,
     69                                           const string& servers,
     70                                           const string& constraints) {
     71   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     72 
     73   base::DictionaryValue* dict = new base::DictionaryValue();
     74   if (!dict)
     75     return;
     76 
     77   dict->SetInteger("rid", render_process_id);
     78   dict->SetInteger("pid", static_cast<int>(pid));
     79   dict->SetInteger("lid", lid);
     80   dict->SetString("servers", servers);
     81   dict->SetString("constraints", constraints);
     82   dict->SetString("url", url);
     83   peer_connection_data_.Append(dict);
     84 
     85   if (observers_.might_have_observers())
     86     SendUpdate("addPeerConnection", dict);
     87 }
     88 
     89 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) {
     90   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     91   for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
     92     base::DictionaryValue* dict = NULL;
     93     peer_connection_data_.GetDictionary(i, &dict);
     94 
     95     int this_pid = 0;
     96     int this_lid = 0;
     97     dict->GetInteger("pid", &this_pid);
     98     dict->GetInteger("lid", &this_lid);
     99 
    100     if (this_pid != static_cast<int>(pid) || this_lid != lid)
    101       continue;
    102 
    103     peer_connection_data_.Remove(i, NULL);
    104 
    105     if (observers_.might_have_observers()) {
    106       base::DictionaryValue id;
    107       id.SetInteger("pid", static_cast<int>(pid));
    108       id.SetInteger("lid", lid);
    109       SendUpdate("removePeerConnection", &id);
    110     }
    111     break;
    112   }
    113 }
    114 
    115 void WebRTCInternals::OnUpdatePeerConnection(
    116     ProcessId pid, int lid, const string& type, const string& value) {
    117   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    118 
    119   for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
    120     base::DictionaryValue* record = NULL;
    121     peer_connection_data_.GetDictionary(i, &record);
    122 
    123     int this_pid = 0, this_lid = 0;
    124     record->GetInteger("pid", &this_pid);
    125     record->GetInteger("lid", &this_lid);
    126 
    127     if (this_pid != static_cast<int>(pid) || this_lid != lid)
    128       continue;
    129 
    130     // Append the update to the end of the log.
    131     base::ListValue* log = EnsureLogList(record);
    132     if (!log)
    133       return;
    134 
    135     base::DictionaryValue* log_entry = new base::DictionaryValue();
    136     if (!log_entry)
    137       return;
    138 
    139     log_entry->SetString("type", type);
    140     log_entry->SetString("value", value);
    141     log->Append(log_entry);
    142 
    143     if (observers_.might_have_observers()) {
    144       base::DictionaryValue update;
    145       update.SetInteger("pid", static_cast<int>(pid));
    146       update.SetInteger("lid", lid);
    147       update.SetString("type", type);
    148       update.SetString("value", value);
    149 
    150       SendUpdate("updatePeerConnection", &update);
    151     }
    152     return;
    153   }
    154 }
    155 
    156 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
    157                                  const base::ListValue& value) {
    158   if (!observers_.might_have_observers())
    159     return;
    160 
    161   base::DictionaryValue dict;
    162   dict.SetInteger("pid", static_cast<int>(pid));
    163   dict.SetInteger("lid", lid);
    164 
    165   base::ListValue* list = value.DeepCopy();
    166   if (!list)
    167     return;
    168 
    169   dict.Set("reports", list);
    170 
    171   SendUpdate("addStats", &dict);
    172 }
    173 
    174 void WebRTCInternals::OnGetUserMedia(int rid,
    175                                      base::ProcessId pid,
    176                                      const std::string& origin,
    177                                      bool audio,
    178                                      bool video,
    179                                      const std::string& audio_constraints,
    180                                      const std::string& video_constraints) {
    181   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    182 
    183   base::DictionaryValue* dict = new base::DictionaryValue();
    184   dict->SetInteger("rid", rid);
    185   dict->SetInteger("pid", static_cast<int>(pid));
    186   dict->SetString("origin", origin);
    187   if (audio)
    188     dict->SetString("audio", audio_constraints);
    189   if (video)
    190     dict->SetString("video", video_constraints);
    191 
    192   get_user_media_requests_.Append(dict);
    193 
    194   if (observers_.might_have_observers())
    195     SendUpdate("addGetUserMedia", dict);
    196 }
    197 
    198 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
    199   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    200   observers_.AddObserver(observer);
    201 }
    202 
    203 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
    204   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    205   observers_.RemoveObserver(observer);
    206 
    207   // Disables the AEC recording if it is enabled and the last webrtc-internals
    208   // page is going away.
    209   if (aec_dump_enabled_ && !observers_.might_have_observers())
    210     DisableAecDump();
    211 }
    212 
    213 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
    214   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    215   if (peer_connection_data_.GetSize() > 0)
    216     observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
    217 
    218   for (base::ListValue::iterator it = get_user_media_requests_.begin();
    219        it != get_user_media_requests_.end();
    220        ++it) {
    221     observer->OnUpdate("addGetUserMedia", *it);
    222   }
    223 }
    224 
    225 void WebRTCInternals::EnableAecDump(content::WebContents* web_contents) {
    226 #if defined(ENABLE_WEBRTC)
    227 #if defined(OS_ANDROID)
    228   EnableAecDumpOnAllRenderProcessHosts();
    229 #else
    230   select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL);
    231   select_file_dialog_->SelectFile(
    232       ui::SelectFileDialog::SELECT_SAVEAS_FILE,
    233       base::string16(),
    234       aec_dump_file_path_,
    235       NULL,
    236       0,
    237       FILE_PATH_LITERAL(""),
    238       web_contents->GetTopLevelNativeWindow(),
    239       NULL);
    240 #endif
    241 #endif
    242 }
    243 
    244 void WebRTCInternals::DisableAecDump() {
    245 #if defined(ENABLE_WEBRTC)
    246   aec_dump_enabled_ = false;
    247 
    248   // Tear down the dialog since the user has unchecked the AEC dump box.
    249   select_file_dialog_ = NULL;
    250 
    251   for (RenderProcessHost::iterator i(
    252            content::RenderProcessHost::AllHostsIterator());
    253        !i.IsAtEnd(); i.Advance()) {
    254     i.GetCurrentValue()->DisableAecDump();
    255   }
    256 #endif
    257 }
    258 
    259 void WebRTCInternals::ResetForTesting() {
    260   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    261   observers_.Clear();
    262   peer_connection_data_.Clear();
    263   get_user_media_requests_.Clear();
    264   aec_dump_enabled_ = false;
    265 }
    266 
    267 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
    268   DCHECK(observers_.might_have_observers());
    269 
    270   FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
    271                     observers_,
    272                     OnUpdate(command, value));
    273 }
    274 
    275 void WebRTCInternals::Observe(int type,
    276                               const NotificationSource& source,
    277                               const NotificationDetails& details) {
    278   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    279   DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
    280   OnRendererExit(Source<RenderProcessHost>(source)->GetID());
    281 }
    282 
    283 void WebRTCInternals::FileSelected(const base::FilePath& path,
    284                                    int /* unused_index */,
    285                                    void* /*unused_params */) {
    286 #if defined(ENABLE_WEBRTC)
    287   aec_dump_file_path_ = path;
    288   EnableAecDumpOnAllRenderProcessHosts();
    289 #endif
    290 }
    291 
    292 void WebRTCInternals::FileSelectionCanceled(void* params) {
    293 #if defined(ENABLE_WEBRTC)
    294   SendUpdate("aecRecordingFileSelectionCancelled", NULL);
    295 #endif
    296 }
    297 
    298 void WebRTCInternals::OnRendererExit(int render_process_id) {
    299   // Iterates from the end of the list to remove the PeerConnections created
    300   // by the exitting renderer.
    301   for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) {
    302     base::DictionaryValue* record = NULL;
    303     peer_connection_data_.GetDictionary(i, &record);
    304 
    305     int this_rid = 0;
    306     record->GetInteger("rid", &this_rid);
    307 
    308     if (this_rid == render_process_id) {
    309       if (observers_.might_have_observers()) {
    310         int lid = 0, pid = 0;
    311         record->GetInteger("lid", &lid);
    312         record->GetInteger("pid", &pid);
    313 
    314         base::DictionaryValue update;
    315         update.SetInteger("lid", lid);
    316         update.SetInteger("pid", pid);
    317         SendUpdate("removePeerConnection", &update);
    318       }
    319       peer_connection_data_.Remove(i, NULL);
    320     }
    321   }
    322 
    323   bool found_any = false;
    324   // Iterates from the end of the list to remove the getUserMedia requests
    325   // created by the exiting renderer.
    326   for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) {
    327     base::DictionaryValue* record = NULL;
    328     get_user_media_requests_.GetDictionary(i, &record);
    329 
    330     int this_rid = 0;
    331     record->GetInteger("rid", &this_rid);
    332 
    333     if (this_rid == render_process_id) {
    334       get_user_media_requests_.Remove(i, NULL);
    335       found_any = true;
    336     }
    337   }
    338 
    339   if (found_any && observers_.might_have_observers()) {
    340     base::DictionaryValue update;
    341     update.SetInteger("rid", render_process_id);
    342     SendUpdate("removeGetUserMediaForRenderer", &update);
    343   }
    344 }
    345 
    346 #if defined(ENABLE_WEBRTC)
    347 void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
    348   aec_dump_enabled_ = true;
    349   for (RenderProcessHost::iterator i(
    350            content::RenderProcessHost::AllHostsIterator());
    351        !i.IsAtEnd(); i.Advance()) {
    352     i.GetCurrentValue()->EnableAecDump(aec_dump_file_path_);
    353   }
    354 }
    355 #endif
    356 
    357 }  // namespace content
    358