root/content/browser/media/webrtc_internals.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. EnsureLogList
  2. GetInstance
  3. OnAddPeerConnection
  4. OnRemovePeerConnection
  5. OnUpdatePeerConnection
  6. OnAddStats
  7. OnGetUserMedia
  8. AddObserver
  9. RemoveObserver
  10. UpdateObserver
  11. EnableAecDump
  12. DisableAecDump
  13. ResetForTesting
  14. SendUpdate
  15. Observe
  16. FileSelected
  17. FileSelectionCanceled
  18. OnRendererExit
  19. EnableAecDumpOnAllRenderProcessHosts

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/media/webrtc_internals.h"

#include "base/command_line.h"
#include "content/browser/media/webrtc_internals_ui_observer.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "content/public/common/content_switches.h"

using base::ProcessId;
using std::string;

namespace content {

namespace {
// Makes sure that |dict| has a ListValue under path "log".
static base::ListValue* EnsureLogList(base::DictionaryValue* dict) {
  base::ListValue* log = NULL;
  if (!dict->GetList("log", &log)) {
    log = new base::ListValue();
    if (log)
      dict->Set("log", log);
  }
  return log;
}

}  // namespace

WebRTCInternals::WebRTCInternals()
    : aec_dump_enabled_(false) {
  registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                 NotificationService::AllBrowserContextsAndSources());
// TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
// build if WebRTC is disabled?
#if defined(ENABLE_WEBRTC)
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableWebRtcAecRecordings)) {
    aec_dump_enabled_ = true;
    aec_dump_file_path_ = CommandLine::ForCurrentProcess()->GetSwitchValuePath(
        switches::kEnableWebRtcAecRecordings);
  } else {
#if defined(OS_CHROMEOS)
    aec_dump_file_path_ =
        base::FilePath(FILE_PATH_LITERAL("/tmp/audio.aecdump"));
#elif defined(OS_ANDROID)
    aec_dump_file_path_ =
        base::FilePath(FILE_PATH_LITERAL("/sdcard/audio.aecdump"));
#else
    aec_dump_file_path_ = base::FilePath(FILE_PATH_LITERAL("audio.aecdump"));
#endif
  }
#endif  // defined(ENABLE_WEBRTC)
}

WebRTCInternals::~WebRTCInternals() {
}

WebRTCInternals* WebRTCInternals::GetInstance() {
  return Singleton<WebRTCInternals>::get();
}

void WebRTCInternals::OnAddPeerConnection(int render_process_id,
                                          ProcessId pid,
                                          int lid,
                                          const string& url,
                                          const string& servers,
                                          const string& constraints) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  base::DictionaryValue* dict = new base::DictionaryValue();
  if (!dict)
    return;

  dict->SetInteger("rid", render_process_id);
  dict->SetInteger("pid", static_cast<int>(pid));
  dict->SetInteger("lid", lid);
  dict->SetString("servers", servers);
  dict->SetString("constraints", constraints);
  dict->SetString("url", url);
  peer_connection_data_.Append(dict);

  if (observers_.might_have_observers())
    SendUpdate("addPeerConnection", dict);
}

void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
    base::DictionaryValue* dict = NULL;
    peer_connection_data_.GetDictionary(i, &dict);

    int this_pid = 0;
    int this_lid = 0;
    dict->GetInteger("pid", &this_pid);
    dict->GetInteger("lid", &this_lid);

    if (this_pid != static_cast<int>(pid) || this_lid != lid)
      continue;

    peer_connection_data_.Remove(i, NULL);

    if (observers_.might_have_observers()) {
      base::DictionaryValue id;
      id.SetInteger("pid", static_cast<int>(pid));
      id.SetInteger("lid", lid);
      SendUpdate("removePeerConnection", &id);
    }
    break;
  }
}

void WebRTCInternals::OnUpdatePeerConnection(
    ProcessId pid, int lid, const string& type, const string& value) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
    base::DictionaryValue* record = NULL;
    peer_connection_data_.GetDictionary(i, &record);

    int this_pid = 0, this_lid = 0;
    record->GetInteger("pid", &this_pid);
    record->GetInteger("lid", &this_lid);

    if (this_pid != static_cast<int>(pid) || this_lid != lid)
      continue;

    // Append the update to the end of the log.
    base::ListValue* log = EnsureLogList(record);
    if (!log)
      return;

    base::DictionaryValue* log_entry = new base::DictionaryValue();
    if (!log_entry)
      return;

    log_entry->SetString("type", type);
    log_entry->SetString("value", value);
    log->Append(log_entry);

    if (observers_.might_have_observers()) {
      base::DictionaryValue update;
      update.SetInteger("pid", static_cast<int>(pid));
      update.SetInteger("lid", lid);
      update.SetString("type", type);
      update.SetString("value", value);

      SendUpdate("updatePeerConnection", &update);
    }
    return;
  }
}

void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
                                 const base::ListValue& value) {
  if (!observers_.might_have_observers())
    return;

  base::DictionaryValue dict;
  dict.SetInteger("pid", static_cast<int>(pid));
  dict.SetInteger("lid", lid);

  base::ListValue* list = value.DeepCopy();
  if (!list)
    return;

  dict.Set("reports", list);

  SendUpdate("addStats", &dict);
}

void WebRTCInternals::OnGetUserMedia(int rid,
                                     base::ProcessId pid,
                                     const std::string& origin,
                                     bool audio,
                                     bool video,
                                     const std::string& audio_constraints,
                                     const std::string& video_constraints) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  base::DictionaryValue* dict = new base::DictionaryValue();
  dict->SetInteger("rid", rid);
  dict->SetInteger("pid", static_cast<int>(pid));
  dict->SetString("origin", origin);
  if (audio)
    dict->SetString("audio", audio_constraints);
  if (video)
    dict->SetString("video", video_constraints);

  get_user_media_requests_.Append(dict);

  if (observers_.might_have_observers())
    SendUpdate("addGetUserMedia", dict);
}

void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  observers_.AddObserver(observer);
}

void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  observers_.RemoveObserver(observer);

  // Disables the AEC recording if it is enabled and the last webrtc-internals
  // page is going away.
  if (aec_dump_enabled_ && !observers_.might_have_observers())
    DisableAecDump();
}

void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (peer_connection_data_.GetSize() > 0)
    observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);

  for (base::ListValue::iterator it = get_user_media_requests_.begin();
       it != get_user_media_requests_.end();
       ++it) {
    observer->OnUpdate("addGetUserMedia", *it);
  }
}

void WebRTCInternals::EnableAecDump(content::WebContents* web_contents) {
#if defined(ENABLE_WEBRTC)
#if defined(OS_ANDROID)
  EnableAecDumpOnAllRenderProcessHosts();
#else
  select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL);
  select_file_dialog_->SelectFile(
      ui::SelectFileDialog::SELECT_SAVEAS_FILE,
      base::string16(),
      aec_dump_file_path_,
      NULL,
      0,
      FILE_PATH_LITERAL(""),
      web_contents->GetView()->GetTopLevelNativeWindow(),
      NULL);
#endif
#endif
}

void WebRTCInternals::DisableAecDump() {
#if defined(ENABLE_WEBRTC)
  aec_dump_enabled_ = false;
  for (RenderProcessHost::iterator i(
           content::RenderProcessHost::AllHostsIterator());
       !i.IsAtEnd(); i.Advance()) {
    i.GetCurrentValue()->DisableAecDump();
  }
#endif
}

void WebRTCInternals::ResetForTesting() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  observers_.Clear();
  peer_connection_data_.Clear();
  get_user_media_requests_.Clear();
  aec_dump_enabled_ = false;
}

void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
  DCHECK(observers_.might_have_observers());

  FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
                    observers_,
                    OnUpdate(command, value));
}

void WebRTCInternals::Observe(int type,
                              const NotificationSource& source,
                              const NotificationDetails& details) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
  OnRendererExit(Source<RenderProcessHost>(source)->GetID());
}

void WebRTCInternals::FileSelected(const base::FilePath& path,
                                   int /* unused_index */,
                                   void* /*unused_params */) {
#if defined(ENABLE_WEBRTC)
  aec_dump_file_path_ = path;
  EnableAecDumpOnAllRenderProcessHosts();
#endif
}

void WebRTCInternals::FileSelectionCanceled(void* params) {
#if defined(ENABLE_WEBRTC)
  SendUpdate("aecRecordingFileSelectionCancelled", NULL);
#endif
}

void WebRTCInternals::OnRendererExit(int render_process_id) {
  // Iterates from the end of the list to remove the PeerConnections created
  // by the exitting renderer.
  for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) {
    base::DictionaryValue* record = NULL;
    peer_connection_data_.GetDictionary(i, &record);

    int this_rid = 0;
    record->GetInteger("rid", &this_rid);

    if (this_rid == render_process_id) {
      if (observers_.might_have_observers()) {
        int lid = 0, pid = 0;
        record->GetInteger("lid", &lid);
        record->GetInteger("pid", &pid);

        base::DictionaryValue update;
        update.SetInteger("lid", lid);
        update.SetInteger("pid", pid);
        SendUpdate("removePeerConnection", &update);
      }
      peer_connection_data_.Remove(i, NULL);
    }
  }

  bool found_any = false;
  // Iterates from the end of the list to remove the getUserMedia requests
  // created by the exiting renderer.
  for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) {
    base::DictionaryValue* record = NULL;
    get_user_media_requests_.GetDictionary(i, &record);

    int this_rid = 0;
    record->GetInteger("rid", &this_rid);

    if (this_rid == render_process_id) {
      get_user_media_requests_.Remove(i, NULL);
      found_any = true;
    }
  }

  if (found_any && observers_.might_have_observers()) {
    base::DictionaryValue update;
    update.SetInteger("rid", render_process_id);
    SendUpdate("removeGetUserMediaForRenderer", &update);
  }
}

#if defined(ENABLE_WEBRTC)
void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
  aec_dump_enabled_ = true;
  for (RenderProcessHost::iterator i(
           content::RenderProcessHost::AllHostsIterator());
       !i.IsAtEnd(); i.Advance()) {
    i.GetCurrentValue()->EnableAecDump(aec_dump_file_path_);
  }
}
#endif

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */