diff --git a/DEPS b/DEPS index 3452ef1a7..36d6f75d0 100644 --- a/DEPS +++ b/DEPS @@ -1,11 +1,11 @@ vars = { "webrtc_trunk" : "https://webrtc.googlecode.com/svn/trunk", "chromium_trunk" : "http://src.chromium.org/svn/trunk", - "chromium_revision": "86252", + "chromium_revision": "95033", # Use this googlecode_url variable only if there is an internal mirror for it. # If you do not know, use the full path while defining your new deps entry. "googlecode_url": "http://%s.googlecode.com/svn", - "libjingle_revision": "59", + "libjingle_revision": "77", } deps = { @@ -43,7 +43,7 @@ deps = { Var("chromium_trunk") + "/src/third_party/libjingle@" + Var("chromium_revision"), "trunk/third_party/libjingle/source": - (Var("googlecode_url") % "libjingle") + "/branches/chrome-sandbox@" + Var("libjingle_revision"), + (Var("googlecode_url") % "libjingle") + "/trunk@" + Var("libjingle_revision"), "trunk/third_party/yasm/source/patched-yasm": Var("chromium_trunk") + "/deps/third_party/yasm/patched-yasm@73761", diff --git a/peerconnection/samples/client/conductor.cc b/peerconnection/samples/client/conductor.cc index a4c104a03..f2d8d1abb 100644 --- a/peerconnection/samples/client/conductor.cc +++ b/peerconnection/samples/client/conductor.cc @@ -12,6 +12,7 @@ #include "peerconnection/samples/client/defaults.h" #include "talk/base/logging.h" +#include "talk/p2p/client/basicportallocator.h" #include "talk/session/phone/videorendererfactory.h" Conductor::Conductor(PeerConnectionClient* client, MainWnd* main_wnd) @@ -19,8 +20,8 @@ Conductor::Conductor(PeerConnectionClient* client, MainWnd* main_wnd) waiting_for_audio_(false), waiting_for_video_(false), peer_id_(-1), - video_channel_(-1), - audio_channel_(-1), + video_channel_(""), + audio_channel_(""), client_(client), main_wnd_(main_wnd) { // Create a window for posting notifications back to from other threads. @@ -37,11 +38,11 @@ Conductor::~Conductor() { } bool Conductor::has_video() const { - return video_channel_ != -1; + return !video_channel_.empty(); } bool Conductor::has_audio() const { - return audio_channel_ != -1; + return !audio_channel_.empty(); } bool Conductor::connection_active() const { @@ -51,6 +52,8 @@ bool Conductor::connection_active() const { void Conductor::Close() { if (peer_connection_.get()) { peer_connection_->Close(); + video_channel_ = ""; + audio_channel_ = ""; } else { client_->SignOut(); } @@ -58,7 +61,25 @@ void Conductor::Close() { bool Conductor::InitializePeerConnection() { ASSERT(peer_connection_.get() == NULL); - peer_connection_.reset(new webrtc::PeerConnection(GetPeerConnectionString())); + ASSERT(port_allocator_.get() == NULL); + ASSERT(worker_thread_.get() == NULL); + + port_allocator_.reset(new cricket::BasicPortAllocator( + new talk_base::BasicNetworkManager(), + talk_base::SocketAddress("stun.l.google.com", 19302), + talk_base::SocketAddress(), + talk_base::SocketAddress(), talk_base::SocketAddress())); + + worker_thread_.reset(new talk_base::Thread()); + if (!worker_thread_->SetName("workder thread", this) || + !worker_thread_->Start()) { + LOG(WARNING) << "Failed to start libjingle workder thread"; + } + + peer_connection_.reset( + webrtc::PeerConnection::Create(GetPeerConnectionString(), + port_allocator_.get(), + worker_thread_.get())); peer_connection_->RegisterObserver(this); if (!peer_connection_->Init()) { DeletePeerConnection(); @@ -91,6 +112,10 @@ void Conductor::StartCaptureDevice() { // PeerConnectionObserver implementation. // +void Conductor::OnInitialized() { + PostMessage(handle(), PEER_CONNECTION_ADDSTREAMS, 0, 0); +} + void Conductor::OnError() { LOG(INFO) << __FUNCTION__; ASSERT(false); @@ -99,7 +124,7 @@ void Conductor::OnError() { void Conductor::OnSignalingMessage(const std::string& msg) { LOG(INFO) << __FUNCTION__; - bool shutting_down = (video_channel_ == -1 && audio_channel_ == -1); + bool shutting_down = (video_channel_.empty() && audio_channel_.empty()); if (handshake_ == OFFER_RECEIVED && !shutting_down) StartCaptureDevice(); @@ -120,38 +145,67 @@ void Conductor::OnSignalingMessage(const std::string& msg) { } } -// Called when a remote stream is added -void Conductor::OnAddStream(const std::string& stream_id, int channel_id, - bool video) { +// Called when a local stream is added and initialized +void Conductor::OnLocalStreamInitialized(const std::string& stream_id, + bool video) { LOG(INFO) << __FUNCTION__ << " " << stream_id; bool send_notification = (waiting_for_video_ || waiting_for_audio_); if (video) { - ASSERT(video_channel_ == -1); - video_channel_ = channel_id; + ASSERT(video_channel_.empty()); + video_channel_ = stream_id; waiting_for_video_ = false; - LOG(INFO) << "Setting video renderer for channel: " << channel_id; + LOG(INFO) << "Setting video renderer for stream: " << stream_id; bool ok = peer_connection_->SetVideoRenderer(stream_id, main_wnd_->remote_renderer()); ASSERT(ok); } else { - ASSERT(audio_channel_ == -1); - audio_channel_ = channel_id; + ASSERT(audio_channel_.empty()); + audio_channel_ = stream_id; waiting_for_audio_ = false; } if (send_notification && !waiting_for_audio_ && !waiting_for_video_) PostMessage(handle(), MEDIA_CHANNELS_INITIALIZED, 0, 0); + + if (!waiting_for_audio_ && !waiting_for_video_) { + PostMessage(handle(), PEER_CONNECTION_CONNECT, 0, 0); + } } -void Conductor::OnRemoveStream(const std::string& stream_id, int channel_id, - bool video) { +// Called when a remote stream is added +void Conductor::OnAddStream(const std::string& stream_id, bool video) { + LOG(INFO) << __FUNCTION__ << " " << stream_id; + bool send_notification = (waiting_for_video_ || waiting_for_audio_); + if (video) { + ASSERT(video_channel_.empty()); + video_channel_ = stream_id; + waiting_for_video_ = false; + LOG(INFO) << "Setting video renderer for stream: " << stream_id; + bool ok = peer_connection_->SetVideoRenderer(stream_id, + main_wnd_->remote_renderer()); + ASSERT(ok); + } else { + ASSERT(audio_channel_.empty()); + audio_channel_ = stream_id; + waiting_for_audio_ = false; + } + + if (send_notification && !waiting_for_audio_ && !waiting_for_video_) + PostMessage(handle(), MEDIA_CHANNELS_INITIALIZED, 0, 0); + + if (!waiting_for_audio_ && !waiting_for_video_) { + PostMessage(handle(), PEER_CONNECTION_CONNECT, 0, 0); + } +} + +void Conductor::OnRemoveStream(const std::string& stream_id, bool video) { LOG(INFO) << __FUNCTION__; if (video) { - ASSERT(channel_id == video_channel_); - video_channel_ = -1; + ASSERT(video_channel_.compare(stream_id) == 0); + video_channel_ = ""; } else { - ASSERT(channel_id == audio_channel_); - audio_channel_ = -1; + ASSERT(audio_channel_.compare(stream_id) == 0); + audio_channel_ = ""; } } @@ -214,9 +268,6 @@ void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) { } else if (handshake_ == INITIATOR) { LOG(INFO) << "Remote peer sent us an answer"; handshake_ = ANSWER_RECEIVED; - } else { - LOG(INFO) << "Remote peer is disconnecting"; - handshake_ = QUIT_SENT; } peer_connection_->SignalingMessage(message); @@ -255,19 +306,24 @@ void Conductor::ConnectToPeer(int peer_id) { if (InitializePeerConnection()) { peer_id_ = peer_id; - waiting_for_video_ = peer_connection_->AddStream(kVideoLabel, true); - waiting_for_audio_ = peer_connection_->AddStream(kAudioLabel, false); - if (waiting_for_video_ || waiting_for_audio_) - handshake_ = INITIATOR; - ASSERT(waiting_for_video_ || waiting_for_audio_); - } - - if (handshake_ == NONE) { + } else { ::MessageBoxA(main_wnd_->handle(), "Failed to initialize PeerConnection", "Error", MB_OK | MB_ICONERROR); } } +void Conductor::AddStreams() { + waiting_for_video_ = peer_connection_->AddStream(kVideoLabel, true); + waiting_for_audio_ = peer_connection_->AddStream(kAudioLabel, false); + if (waiting_for_video_ || waiting_for_audio_) + handshake_ = INITIATOR; + ASSERT(waiting_for_video_ || waiting_for_audio_); +} + +void Conductor::PeerConnectionConnect() { + peer_connection_->Connect(); +} + void Conductor::DisconnectFromCurrentPeer() { if (peer_connection_.get()) peer_connection_->Close(); @@ -295,8 +351,8 @@ bool Conductor::OnMessage(UINT msg, WPARAM wp, LPARAM lp, waiting_for_audio_ = false; waiting_for_video_ = false; peer_id_ = -1; - ASSERT(video_channel_ == -1); - ASSERT(audio_channel_ == -1); + ASSERT(video_channel_.empty()); + ASSERT(audio_channel_.empty()); if (main_wnd_->IsWindow()) { if (client_->is_connected()) { main_wnd_->SwitchToPeerList(client_->peers()); @@ -313,6 +369,10 @@ bool Conductor::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LOG(LS_ERROR) << "SendToPeer failed"; DisconnectFromServer(); } + } else if (msg == PEER_CONNECTION_ADDSTREAMS) { + AddStreams(); + } else if (msg == PEER_CONNECTION_CONNECT) { + PeerConnectionConnect(); } else { ret = false; } diff --git a/peerconnection/samples/client/conductor.h b/peerconnection/samples/client/conductor.h index 3fd3daa6f..01a36b49f 100644 --- a/peerconnection/samples/client/conductor.h +++ b/peerconnection/samples/client/conductor.h @@ -16,7 +16,7 @@ #include "peerconnection/samples/client/main_wnd.h" #include "peerconnection/samples/client/peer_connection_client.h" -#include "talk/app/peerconnection.h" +#include "talk/app/webrtc/peerconnection.h" #include "talk/base/scoped_ptr.h" namespace cricket { @@ -33,6 +33,8 @@ class Conductor MEDIA_CHANNELS_INITIALIZED = WM_APP + 1, PEER_CONNECTION_CLOSED, SEND_MESSAGE_TO_PEER, + PEER_CONNECTION_ADDSTREAMS, + PEER_CONNECTION_CONNECT, }; enum HandshakeState { @@ -56,20 +58,25 @@ class Conductor bool InitializePeerConnection(); void DeletePeerConnection(); void StartCaptureDevice(); + void AddStreams(); + void PeerConnectionConnect(); // // PeerConnectionObserver implementation. // + virtual void OnInitialized(); virtual void OnError(); virtual void OnSignalingMessage(const std::string& msg); + // Called when a local stream is added and initialized + virtual void OnLocalStreamInitialized(const std::string& stream_id, + bool video); + // Called when a remote stream is added - virtual void OnAddStream(const std::string& stream_id, int channel_id, - bool video); + virtual void OnAddStream(const std::string& stream_id, bool video); virtual void OnRemoveStream(const std::string& stream_id, - int channel_id, bool video); // @@ -111,10 +118,12 @@ class Conductor bool waiting_for_video_; int peer_id_; talk_base::scoped_ptr peer_connection_; + talk_base::scoped_ptr port_allocator_; + talk_base::scoped_ptr worker_thread_; PeerConnectionClient* client_; MainWnd* main_wnd_; - int video_channel_; - int audio_channel_; + std::string video_channel_; + std::string audio_channel_; }; #endif // PEERCONNECTION_SAMPLES_CLIENT_CONDUCTOR_H_ diff --git a/peerconnection/samples/client/main_wnd.cc b/peerconnection/samples/client/main_wnd.cc index b6b125fd3..416b6dbfd 100644 --- a/peerconnection/samples/client/main_wnd.cc +++ b/peerconnection/samples/client/main_wnd.cc @@ -168,53 +168,68 @@ void MainWnd::OnPaint() { long height = abs(bmi.bmiHeader.biHeight); long width = bmi.bmiHeader.biWidth; - HDC dc_mem = ::CreateCompatibleDC(ps.hdc); + if (remote_video_.get()->image() != NULL) { + HDC dc_mem = ::CreateCompatibleDC(ps.hdc); - // Set the map mode so that the ratio will be maintained for us. - HDC all_dc[] = { ps.hdc, dc_mem }; - for (int i = 0; i < ARRAY_SIZE(all_dc); ++i) { - SetMapMode(all_dc[i], MM_ISOTROPIC); - SetWindowExtEx(all_dc[i], width, height, NULL); - SetViewportExtEx(all_dc[i], rc.right, rc.bottom, NULL); + // Set the map mode so that the ratio will be maintained for us. + HDC all_dc[] = { ps.hdc, dc_mem }; + for (int i = 0; i < ARRAY_SIZE(all_dc); ++i) { + SetMapMode(all_dc[i], MM_ISOTROPIC); + SetWindowExtEx(all_dc[i], width, height, NULL); + SetViewportExtEx(all_dc[i], rc.right, rc.bottom, NULL); + } + + HBITMAP bmp_mem = ::CreateCompatibleBitmap(ps.hdc, rc.right, rc.bottom); + HGDIOBJ bmp_old = ::SelectObject(dc_mem, bmp_mem); + + POINT logical_area = { rc.right, rc.bottom }; + DPtoLP(ps.hdc, &logical_area, 1); + + HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0)); + RECT logical_rect = {0, 0, logical_area.x, logical_area.y }; + ::FillRect(dc_mem, &logical_rect, brush); + ::DeleteObject(brush); + + const uint8* image = remote_video_->image(); + int max_unit = std::max(width, height); + int x = (logical_area.x / 2) - (width / 2); + int y = (logical_area.y / 2) - (height / 2); + + StretchDIBits(dc_mem, x, y, width, height, + 0, 0, width, height, image, &bmi, DIB_RGB_COLORS, SRCCOPY); + + if ((rc.right - rc.left) > 200 && (rc.bottom - rc.top) > 200) { + const BITMAPINFO& bmi = local_video_->bmi(); + image = local_video_->image(); + long thumb_width = bmi.bmiHeader.biWidth / 4; + long thumb_height = abs(bmi.bmiHeader.biHeight) / 4; + StretchDIBits(dc_mem, + logical_area.x - thumb_width - 10, + logical_area.y - thumb_height - 10, + thumb_width, thumb_height, + 0, 0, bmi.bmiHeader.biWidth, -bmi.bmiHeader.biHeight, + image, &bmi, DIB_RGB_COLORS, SRCCOPY); + } + + BitBlt(ps.hdc, 0, 0, logical_area.x, logical_area.y, + dc_mem, 0, 0, SRCCOPY); + + // Cleanup. + ::SelectObject(dc_mem, bmp_old); + ::DeleteObject(bmp_mem); + ::DeleteDC(dc_mem); + } else { + // We're still waiting for the video stream to be initialized. + HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0)); + ::FillRect(ps.hdc, &rc, brush); + ::DeleteObject(brush); + HGDIOBJ old_font = ::SelectObject(ps.hdc, GetDefaultFont()); + ::SetTextColor(ps.hdc, RGB(0xff, 0xff, 0xff)); + ::SetBkMode(ps.hdc, TRANSPARENT); + ::DrawTextA(ps.hdc, "Connecting...", -1, &rc, + DT_SINGLELINE | DT_CENTER | DT_VCENTER); + ::SelectObject(ps.hdc, old_font); } - - HBITMAP bmp_mem = ::CreateCompatibleBitmap(ps.hdc, rc.right, rc.bottom); - HGDIOBJ bmp_old = ::SelectObject(dc_mem, bmp_mem); - HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0)); - ::FillRect(dc_mem, &rc, brush); - ::DeleteObject(brush); - - POINT logical_area = { rc.right, rc.bottom }; - DPtoLP(ps.hdc, &logical_area, 1); - - const uint8* image = remote_video_->image(); - int max_unit = std::max(width, height); - int x = (logical_area.x / 2) - (width / 2); - int y = (logical_area.y / 2) - (height / 2); - - StretchDIBits(dc_mem, x, y, width, height, - 0, 0, width, height, image, &bmi, DIB_RGB_COLORS, SRCCOPY); - - if ((rc.right - rc.left) > 200 && (rc.bottom - rc.top) > 200) { - const BITMAPINFO& bmi = local_video_->bmi(); - image = local_video_->image(); - long thumb_width = bmi.bmiHeader.biWidth / 4; - long thumb_height = abs(bmi.bmiHeader.biHeight) / 4; - StretchDIBits(dc_mem, - logical_area.x - thumb_width - 10, - logical_area.y - thumb_height - 10, - thumb_width, thumb_height, - 0, 0, bmi.bmiHeader.biWidth, -bmi.bmiHeader.biHeight, - image, &bmi, DIB_RGB_COLORS, SRCCOPY); - } - - BitBlt(ps.hdc, 0, 0, logical_area.x, logical_area.y, - dc_mem, 0, 0, SRCCOPY); - - // Cleanup. - ::SelectObject(dc_mem, bmp_old); - ::DeleteObject(bmp_mem); - ::DeleteDC(dc_mem); } else { HBRUSH brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW)); ::FillRect(ps.hdc, &rc, brush); @@ -481,7 +496,6 @@ MainWnd::VideoRenderer::VideoRenderer(HWND wnd, int width, int height) bmi_.bmiHeader.biHeight = -height; bmi_.bmiHeader.biSizeImage = width * height * (bmi_.bmiHeader.biBitCount >> 3); - image_.reset(new uint8[bmi_.bmiHeader.biSizeImage]); } MainWnd::VideoRenderer::~VideoRenderer() { @@ -529,6 +543,7 @@ void MainWnd::VideoRenderer::OnMessage(const MSG& msg) { break; case WM_PAINT: { + ASSERT(image_.get() != NULL); const cricket::VideoFrame* frame = reinterpret_cast(msg.lParam); frame->ConvertToRgbBuffer(cricket::FOURCC_ARGB, image_.get(), diff --git a/third_party_mods/libjingle/libjingle.gyp b/third_party_mods/libjingle/libjingle.gyp index f8793d934..5794b4e1c 100644 --- a/third_party_mods/libjingle/libjingle.gyp +++ b/third_party_mods/libjingle/libjingle.gyp @@ -8,6 +8,7 @@ # We declare a default value of 0 for standalone builds. 'inside_chromium_build%': 0, 'no_libjingle_logging%': 0, + 'peer_connection_dev%': 0, }, 'target_defaults': { 'defines': [ @@ -16,6 +17,7 @@ '_USE_32BIT_TIME_T', 'SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS', 'EXPAT_RELATIVE_PATH', + 'WEBRTC_RELATIVE_PATH', 'HAVE_WEBRTC', ], 'configurations': { @@ -30,11 +32,15 @@ 'dependencies': [ '../expat/expat.gyp:expat', ], + 'export_dependent_settings': [ + '../expat/expat.gyp:expat', + ], 'direct_dependent_settings': { 'defines': [ 'FEATURE_ENABLE_SSL', 'FEATURE_ENABLE_VOICEMAIL', 'EXPAT_RELATIVE_PATH', + 'WEBRTC_RELATIVE_PATH', ], 'conditions': [ ['OS=="win"', { @@ -64,7 +70,7 @@ 'OSX', ], }], - ['OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="openbsd"', { + ['os_posix == 1', { 'defines': [ 'POSIX', ], @@ -114,7 +120,7 @@ },{ 'include_dirs': [ # the third_party folder for webrtc/ includes (non-chromium). - '../../trunk', + '../../src', './source', '../../third_party/expat/files', ], @@ -134,7 +140,7 @@ 'OSX', ], }], - ['OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="openbsd"', { + ['os_posix == 1', { 'defines': [ 'POSIX', ], @@ -158,7 +164,7 @@ }], ], }, - 'type': '<(library)', + 'type': 'static_library', 'sources': [ '<(overrides)/talk/base/basictypes.h', '<(overrides)/talk/base/constructormagic.h', @@ -353,7 +359,7 @@ 'source/talk/base/winping.h', ], }], - ['OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="openbsd"', { + ['os_posix == 1', { 'sources': [ 'source/talk/base/latebindingsymboltable.cc', 'source/talk/base/latebindingsymboltable.h', @@ -463,6 +469,8 @@ 'source/talk/session/phone/codec.cc', 'source/talk/session/phone/codec.h', 'source/talk/session/phone/cryptoparams.h', + 'source/talk/session/phone/currentspeakermonitor.cc', + 'source/talk/session/phone/currentspeakermonitor.h', 'source/talk/session/phone/devicemanager.cc', 'source/talk/session/phone/devicemanager.h', 'source/talk/session/phone/filemediaengine.cc', @@ -489,6 +497,15 @@ 'source/talk/session/phone/srtpfilter.h', 'source/talk/session/phone/videocommon.h', 'source/talk/session/phone/voicechannel.h', + 'source/talk/session/phone/webrtccommon.h', + 'source/talk/session/phone/webrtcvideoengine.cc', + 'source/talk/session/phone/webrtcvideoengine.h', + 'source/talk/session/phone/webrtcvideoframe.cc', + 'source/talk/session/phone/webrtcvideoframe.h', + 'source/talk/session/phone/webrtcvie.h', + 'source/talk/session/phone/webrtcvoe.h', + 'source/talk/session/phone/webrtcvoiceengine.cc', + 'source/talk/session/phone/webrtcvoiceengine.h', 'source/talk/session/tunnel/pseudotcpchannel.cc', 'source/talk/session/tunnel/pseudotcpchannel.h', 'source/talk/session/tunnel/tunnelsessionclient.cc', @@ -503,8 +520,8 @@ }], ['OS=="linux"', { 'sources': [ - #'source/talk/session/phone/gtkvideorenderer.cc', - #'source/talk/session/phone/gtkvideorenderer.h', + 'source/talk/session/phone/gtkvideorenderer.cc', + 'source/talk/session/phone/gtkvideorenderer.h', 'source/talk/session/phone/libudevsymboltable.cc', 'source/talk/session/phone/libudevsymboltable.h', 'source/talk/session/phone/v4llookup.cc', @@ -513,21 +530,29 @@ 'include_dirs': [ 'source/talk/third_party/libudev', ], + 'cflags': [ + 'SignalRequestSignaling.connect( - this, &P2PTransportManager::OnRequestSignaling); - channel_->SignalWritableState.connect( - this, &P2PTransportManager::OnReadableState); - channel_->SignalWritableState.connect( - this, &P2PTransportManager::OnWriteableState); - channel_->SignalCandidateReady.connect( - this, &P2PTransportManager::OnCandidateReady); - - channel_->Connect(); - return true; -} - -bool P2PTransportManager::AddRemoteCandidate( - const cricket::Candidate& candidate) { - channel_->OnCandidate(candidate); - return true; -} - -cricket::P2PTransportChannel* P2PTransportManager::GetP2PChannel() { - return channel_.get(); -} - -void P2PTransportManager::OnRequestSignaling() { - channel_->OnSignalingReady(); -} - -void P2PTransportManager::OnCandidateReady( - cricket::TransportChannelImpl* channel, - const cricket::Candidate& candidate) { - event_handler_->OnCandidateReady(candidate); -} - -void P2PTransportManager::OnReadableState(cricket::TransportChannel* channel) { - state_ = static_cast(state_ | STATE_READABLE); - event_handler_->OnStateChange(state_); -} - -void P2PTransportManager::OnWriteableState(cricket::TransportChannel* channel) { - state_ = static_cast(state_ | STATE_WRITABLE); - event_handler_->OnStateChange(state_); -} - -} diff --git a/third_party_mods/libjingle/source/talk/app/p2p_transport_manager.h b/third_party_mods/libjingle/source/talk/app/p2p_transport_manager.h deleted file mode 100644 index ea60ad8e2..000000000 --- a/third_party_mods/libjingle/source/talk/app/p2p_transport_manager.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2011 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. - -#ifndef TALK_APP_WEBRTC_P2P_TRANSPORT_MANAGER_H_ -#define TALK_APP_WEBRTC_P2P_TRANSPORT_MANAGER_H_ - -#include - -#include "talk/base/scoped_ptr.h" -#include "talk/base/sigslot.h" - -namespace cricket { -class Candidate; -class P2PTransportChannel; -class PortAllocator; -class TransportChannel; -class TransportChannelImpl; -} - -namespace talk_base { -class NetworkManager; -class PacketSocketFactory; -} - -namespace webrtc { -class P2PTransportManager : public sigslot::has_slots<>{ - public: - enum State { - STATE_NONE = 0, - STATE_WRITABLE = 1, - STATE_READABLE = 2, - }; - - enum Protocol { - PROTOCOL_UDP = 0, - PROTOCOL_TCP = 1, - }; - - class EventHandler { - public: - virtual ~EventHandler() {} - - // Called for each local candidate. - virtual void OnCandidateReady(const cricket::Candidate& candidate) = 0; - - // Called when readable of writable state of the stream changes. - virtual void OnStateChange(State state) = 0; - - // Called when an error occures (e.g. TCP handshake - // failed). P2PTransportManager object is not usable after that and - // should be destroyed. - virtual void OnError(int error) = 0; - }; - - public: - // Create P2PTransportManager using specified NetworkManager and - // PacketSocketFactory. Takes ownership of |network_manager| and - // |socket_factory|. - P2PTransportManager(cricket::PortAllocator* allocator); - ~P2PTransportManager(); - - bool Init(const std::string& name, - Protocol protocol, - const std::string& config, - EventHandler* event_handler); - bool AddRemoteCandidate(const cricket::Candidate& address); - cricket::P2PTransportChannel* GetP2PChannel(); - - private: - - void OnRequestSignaling(); - void OnCandidateReady(cricket::TransportChannelImpl* channel, - const cricket::Candidate& candidate); - void OnReadableState(cricket::TransportChannel* channel); - void OnWriteableState(cricket::TransportChannel* channel); - - std::string name_; - EventHandler* event_handler_; - State state_; - - cricket::PortAllocator* allocator_; - talk_base::scoped_ptr channel_; -}; - -} -#endif // TALK_APP_WEBRTC_P2P_TRANSPORT_MANAGER_H_ diff --git a/third_party_mods/libjingle/source/talk/app/pc_transport_impl.cc b/third_party_mods/libjingle/source/talk/app/pc_transport_impl.cc deleted file mode 100644 index 4029785ef..000000000 --- a/third_party_mods/libjingle/source/talk/app/pc_transport_impl.cc +++ /dev/null @@ -1,359 +0,0 @@ -/* - * pc_transport_impl.cc - * - * Created on: May 2, 2011 - * Author: mallinath - */ - -#include "talk/app/pc_transport_impl.h" - -#ifdef PLATFORM_CHROMIUM -#include "base/values.h" -#include "content/common/json_value_serializer.h" -#include "content/renderer/p2p/p2p_transport_impl.h" -#include "jingle/glue/thread_wrapper.h" -#include "net/base/io_buffer.h" -#include "net/socket/socket.h" -#else -#include "talk/app/p2p_transport_manager.h" -#endif -#include "talk/p2p/base/transportchannel.h" -#include "talk/app/webrtcsessionimpl.h" -#include "talk/app/peerconnection.h" - -namespace webrtc { -enum { - MSG_RTC_ONREADPACKET = 1, - MSG_RTC_TRANSPORTINIT, - MSG_RTC_ADDREMOTECANDIDATE, - MSG_RTC_ONCANDIDATEREADY, -}; - -struct MediaDataMsgParams : public talk_base::MessageData { - MediaDataMsgParams(cricket::TransportChannel* channel, - const char* dataPtr, - int len) - : channel(channel), data(dataPtr), len(len) {} - - cricket::TransportChannel* channel; - const char* data; - int len; -}; - -PC_Transport_Impl::PC_Transport_Impl (WebRTCSessionImpl* session) - : session_(session), -#ifdef PLATFORM_CHROMIUM - ALLOW_THIS_IN_INITIALIZER_LIST( - channel_read_callback_(this, &PC_Transport_Impl::OnRead)), - ALLOW_THIS_IN_INITIALIZER_LIST( - channel_write_callback_(this, &PC_Transport_Impl::OnWrite)), -#endif - writable_(false), - event_(false, false), - network_thread_jingle_(session_->connection()->media_thread()) -{ -#ifdef PLATFORM_CHROMIUM - // Before proceeding, ensure we have libjingle thread wrapper for - // the current thread. - jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); - network_thread_chromium_ = talk_base::Thread::Current(); -#endif - event_.Set(); -} - -PC_Transport_Impl::~PC_Transport_Impl() { -} - -bool PC_Transport_Impl::Init(const std::string& name) { -#ifdef PLATFORM_CHROMIUM - if(network_thread_chromium_ != talk_base::Thread::Current()) { - network_thread_chromium_->Post(this, MSG_RTC_TRANSPORTINIT, - new talk_base::TypedMessageData (name)); - return true; - } -#else - if(network_thread_jingle_ != talk_base::Thread::Current()) { - network_thread_jingle_->Send(this, MSG_RTC_TRANSPORTINIT, - new talk_base::TypedMessageData (name)); - return true; - } -#endif - - name_ = name; - p2p_transport_.reset(CreateP2PTransport()); - -#ifdef PLATFORM_CHROMIUM - webkit_glue::P2PTransport::Protocol protocol = - webkit_glue::P2PTransport::PROTOCOL_UDP; -#else - webrtc::P2PTransportManager::Protocol protocol = - webrtc::P2PTransportManager::PROTOCOL_UDP; -#endif - p2p_transport_->Init(name_, protocol, "", this); - -#ifdef PLATFORM_CHROMIUM - StreamRead(); -#endif - - return true; -} - -#ifdef PLATFORM_CHROMIUM - -void PC_Transport_Impl::OnCandidateReady(const std::string& address) { - if(network_thread_chromium_ != talk_base::Thread::Current()) { - network_thread_chromium_->Post(this, MSG_RTC_ONCANDIDATEREADY, - new talk_base::TypedMessageData ( - address)); - return; - } - - // using only first candidate - // use p2p_transport_impl.cc Deserialize method - cricket::Candidate candidate; - if (local_candidates_.empty()) { - cricket::Candidate candidate; - DeserializeCandidate(address, &candidate); - local_candidates_.push_back(candidate); - session_->OnCandidateReady(candidate); - } -} - -bool PC_Transport_Impl::AddRemoteCandidate( - const cricket::Candidate& candidate) { - if(network_thread_chromium_ != talk_base::Thread::Current()) { - network_thread_chromium_->Post(this, MSG_RTC_ADDREMOTECANDIDATE, - new talk_base::TypedMessageData ( - &candidate)); - // TODO: save the result - return true; - } - - if (!p2p_transport_.get()) - return false; - - return p2p_transport_->AddRemoteCandidate(SerializeCandidate(candidate)); -} - -#else - -void PC_Transport_Impl::OnCandidateReady(const cricket::Candidate& candidate) { - if(network_thread_jingle_ != talk_base::Thread::Current()) { - network_thread_jingle_->Send(this, MSG_RTC_ONCANDIDATEREADY, - new talk_base::TypedMessageData ( - &candidate)); - return; - } - - if (local_candidates_.empty()) { - local_candidates_.push_back(candidate); - session_->OnCandidateReady(candidate); - } -} - -bool PC_Transport_Impl::AddRemoteCandidate( - const cricket::Candidate& candidate) { - if(network_thread_jingle_ != talk_base::Thread::Current()) { - network_thread_jingle_->Send(this, MSG_RTC_ADDREMOTECANDIDATE, - new talk_base::TypedMessageData ( - &candidate)); - // TODO: save the result - return true; - } - - if (!p2p_transport_.get()) - return false; - - return p2p_transport_->AddRemoteCandidate(candidate); -} - -#endif - -#ifdef PLATFORM_CHROMIUM - -int32 PC_Transport_Impl::DoRecv() { - if (!p2p_transport_.get()) - return -1; - - net::Socket* channel = p2p_transport_->GetChannel(); - if (!channel) - return -1; - - scoped_refptr buffer = - new net::WrappedIOBuffer(static_cast(recv_buffer_)); - int result = channel->Read( - buffer, kMaxRtpRtcpPacketLen, &channel_read_callback_); - return result; -} - -void PC_Transport_Impl::OnRead(int result) { - network_thread_jingle_->Post( - this, MSG_RTC_ONREADPACKET, new MediaDataMsgParams( - GetP2PChannel(), recv_buffer_, result)); - StreamRead(); -} - -void PC_Transport_Impl::OnWrite(int result) { - return; -} - -net::Socket* PC_Transport_Impl::GetChannel() { - if (!p2p_transport_.get()) - return NULL; - - return p2p_transport_->GetChannel(); -} - -void PC_Transport_Impl::StreamRead() { - event_.Wait(talk_base::kForever); - DoRecv(); -} - -void PC_Transport_Impl::OnReadPacket_w(cricket::TransportChannel* channel, - const char* data, - size_t len) { - session()->SignalReadPacket(channel, data, len); - event_.Set(); - return ; -} - -std::string PC_Transport_Impl::SerializeCandidate( - const cricket::Candidate& candidate) { - // TODO(sergeyu): Use SDP to format candidates? - DictionaryValue value; - value.SetString("name", candidate.name()); - value.SetString("ip", candidate.address().IPAsString()); - value.SetInteger("port", candidate.address().port()); - value.SetString("type", candidate.type()); - value.SetString("protocol", candidate.protocol()); - value.SetString("username", candidate.username()); - value.SetString("password", candidate.password()); - value.SetDouble("preference", candidate.preference()); - value.SetInteger("generation", candidate.generation()); - - std::string result; - JSONStringValueSerializer serializer(&result); - serializer.Serialize(value); - return result; -} - -bool PC_Transport_Impl::DeserializeCandidate(const std::string& address, - cricket::Candidate* candidate) { - JSONStringValueSerializer deserializer(address); - scoped_ptr value(deserializer.Deserialize(NULL, NULL)); - if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) { - return false; - } - - DictionaryValue* dic_value = static_cast(value.get()); - - std::string name; - std::string ip; - int port; - std::string type; - std::string protocol; - std::string username; - std::string password; - double preference; - int generation; - - if (!dic_value->GetString("name", &name) || - !dic_value->GetString("ip", &ip) || - !dic_value->GetInteger("port", &port) || - !dic_value->GetString("type", &type) || - !dic_value->GetString("protocol", &protocol) || - !dic_value->GetString("username", &username) || - !dic_value->GetString("password", &password) || - !dic_value->GetDouble("preference", &preference) || - !dic_value->GetInteger("generation", &generation)) { - return false; - } - - candidate->set_name(name); - candidate->set_address(talk_base::SocketAddress(ip, port)); - candidate->set_type(type); - candidate->set_protocol(protocol); - candidate->set_username(username); - candidate->set_password(password); - candidate->set_preference(static_cast(preference)); - candidate->set_generation(generation); - - return true; -} -#endif - -void PC_Transport_Impl::OnStateChange(P2PTransportClass::State state) { - writable_ = (state | P2PTransportClass::STATE_WRITABLE) != 0; - if (writable_) { - session_->OnStateChange(state, p2p_transport()->GetP2PChannel()); - } -} - -void PC_Transport_Impl::OnError(int error) { - -} - -cricket::TransportChannel* PC_Transport_Impl::GetP2PChannel() { - if (!p2p_transport_.get()) - return NULL; - - return p2p_transport_->GetP2PChannel(); -} - -void PC_Transport_Impl::OnMessage(talk_base::Message* message) { - talk_base::MessageData* data = message->pdata; - switch(message->message_id) { - case MSG_RTC_TRANSPORTINIT : { - talk_base::TypedMessageData *p = - static_cast* >(data); - Init(p->data()); - delete p; - break; - } - case MSG_RTC_ADDREMOTECANDIDATE : { - talk_base::TypedMessageData *p = - static_cast* >(data); - AddRemoteCandidate(*p->data()); - delete p; - break; - } -#ifdef PLATFORM_CHROMIUM - case MSG_RTC_ONCANDIDATEREADY : { - talk_base::TypedMessageData *p = - static_cast* >(data); - OnCandidateReady(p->data()); - delete p; - break; - } - case MSG_RTC_ONREADPACKET : { - MediaDataMsgParams* p = static_cast (data); - ASSERT (p != NULL); - OnReadPacket_w(p->channel, p->data, p->len); - delete data; - break; - } -#else - case MSG_RTC_ONCANDIDATEREADY : { - talk_base::TypedMessageData *p = - static_cast* >(data); - OnCandidateReady(*p->data()); - delete p; - break; - } -#endif - default: - ASSERT(false); - } -} - -P2PTransportClass* PC_Transport_Impl::CreateP2PTransport() { -#ifdef PLATFORM_CHROMIUM - return new P2PTransportImpl( - session()->connection()->p2p_socket_dispatcher()); -#else - return new P2PTransportManager(session()->port_allocator()); -#endif -} - -} //namespace webrtc - diff --git a/third_party_mods/libjingle/source/talk/app/pc_transport_impl.h b/third_party_mods/libjingle/source/talk/app/pc_transport_impl.h deleted file mode 100644 index 95933f2d8..000000000 --- a/third_party_mods/libjingle/source/talk/app/pc_transport_impl.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * peerconnection_transport_impl.h - * - * Created on: May 2, 2011 - * Author: mallinath - */ - -#ifndef TALK_APP_PEERCONNECTION_TRANSPORT_IMPL_H_ -#define TALK_APP_PEERCONNECTION_TRANSPORT_IMPL_H_ - -#include - -#include "talk/base/thread.h" -#include "talk/base/event.h" -#include "talk/base/messagehandler.h" -#include "talk/base/scoped_ptr.h" - -#ifdef PLATFORM_CHROMIUM -#include "net/base/completion_callback.h" -#include "webkit/glue/p2p_transport.h" -class P2PTransportImpl; -#else -#include "talk/app/p2p_transport_manager.h" -#endif - -#ifdef PLATFORM_CHROMIUM -typedef P2PTransportImpl TransportImplClass; -typedef webkit_glue::P2PTransport::EventHandler TransportEventHandler; -typedef webkit_glue::P2PTransport P2PTransportClass; -#else -typedef webrtc::P2PTransportManager TransportImplClass; -typedef webrtc::P2PTransportManager::EventHandler TransportEventHandler; -typedef webrtc::P2PTransportManager P2PTransportClass; -#endif - -namespace cricket { -class TransportChannel; -class Candidate; -} - -namespace webrtc { - -const int kMaxRtpRtcpPacketLen = 1500; - -class WebRTCSessionImpl; -// PC - PeerConnection -class PC_Transport_Impl : public talk_base::MessageHandler, - public TransportEventHandler { - public: - PC_Transport_Impl(WebRTCSessionImpl* session); - virtual ~PC_Transport_Impl(); - - bool Init(const std::string& name); -#ifdef PLATFORM_CHROMIUM - virtual void OnCandidateReady(const std::string& address); -#else - virtual void OnCandidateReady(const cricket::Candidate& candidate); -#endif - virtual void OnStateChange(P2PTransportClass::State state); - virtual void OnError(int error); - -#ifdef PLATFORM_CHROMIUM - void OnRead(int result); - void OnWrite(int result); - net::Socket* GetChannel(); -#endif - - void OnMessage(talk_base::Message* message); - cricket::TransportChannel* GetP2PChannel(); - bool AddRemoteCandidate(const cricket::Candidate& candidate); - WebRTCSessionImpl* session() { return session_; } - P2PTransportClass* p2p_transport() { return p2p_transport_.get(); } - const std::string& name() { return name_; } - std::vector& local_candidates() { - return local_candidates_; - } - - private: - void MsgSend(uint32 id); - P2PTransportClass* CreateP2PTransport(); -#ifdef PLATFORM_CHROMIUM - void OnReadPacket_w( - cricket::TransportChannel* channel, const char* data, size_t len); - int32 DoRecv(); - void StreamRead(); - std::string SerializeCandidate(const cricket::Candidate& candidate); - bool DeserializeCandidate(const std::string& address, - cricket::Candidate* candidate); -#endif - - std::string name_; - WebRTCSessionImpl* session_; - talk_base::scoped_ptr p2p_transport_; - std::vector local_candidates_; - -#ifdef PLATFORM_CHROMIUM - net::CompletionCallbackImpl channel_read_callback_; - net::CompletionCallbackImpl channel_write_callback_; - talk_base::Thread* network_thread_chromium_; -#endif - bool writable_; - char recv_buffer_[kMaxRtpRtcpPacketLen]; - talk_base::Event event_; - talk_base::Thread* network_thread_jingle_; -}; - -} // namespace webrtc - -#endif /* TALK_APP_PEERCONNECTION_TRANSPORT_IMPL_H_ */ diff --git a/third_party_mods/libjingle/source/talk/app/peerconnection.cc b/third_party_mods/libjingle/source/talk/app/peerconnection.cc deleted file mode 100644 index 87805c180..000000000 --- a/third_party_mods/libjingle/source/talk/app/peerconnection.cc +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// Author: mallinath@google.com (Mallinath Bareddy) - -#include - -#include "talk/app/peerconnection.h" - -#include "talk/base/basicpacketsocketfactory.h" -#include "talk/base/helpers.h" -#include "talk/base/stringencode.h" -#include "talk/base/logging.h" - -#include "talk/p2p/client/basicportallocator.h" -#include "talk/session/phone/mediasessionclient.h" -#include "talk/app/webrtcsessionimpl.h" -#include "talk/app/webrtc_json.h" - -namespace webrtc { - -static const size_t kConfigTokens = 2; -static const int kDefaultStunPort = 3478; - -#ifdef PLATFORM_CHROMIUM -PeerConnection::PeerConnection(const std::string& config, - P2PSocketDispatcher* p2p_socket_dispatcher) -#else -PeerConnection::PeerConnection(const std::string& config) -#endif // PLATFORM_CHROMIUM - : config_(config) - ,media_thread_(new talk_base::Thread) - ,network_manager_(new talk_base::NetworkManager) - ,signaling_thread_(new talk_base::Thread) - ,initialized_(false) - ,service_type_(SERVICE_COUNT) - ,event_callback_(NULL) - ,session_(NULL) - ,incoming_(false) -#ifdef PLATFORM_CHROMIUM - ,p2p_socket_dispatcher_(p2p_socket_dispatcher) -#endif // PLATFORM_CHROMIUM -{ -} - -PeerConnection::~PeerConnection() { - if (session_ != NULL) { - // Before deleting the session, make sure that the signaling thread isn't - // running (or wait for it if it is). - signaling_thread_.reset(); - - ASSERT(!session_->HasAudioStream()); - ASSERT(!session_->HasVideoStream()); - // TODO: the RemoveAllStreams has to be asynchronous. At the same - //time "delete session_" should be called after RemoveAllStreams completed. - delete session_; - } -} - -bool PeerConnection::Init() { - ASSERT(!initialized_); - - std::vector tokens; - talk_base::tokenize(config_, ' ', &tokens); - - if (tokens.size() != kConfigTokens) { - LOG(LS_ERROR) << "Invalid config string"; - return false; - } - - service_type_ = SERVICE_COUNT; - - // NOTE: Must be in the same order as the enum. - static const char* kValidServiceTypes[SERVICE_COUNT] = { - "STUN", "STUNS","TURN", "TURNS" - }; - const std::string& type = tokens[0]; - for (size_t i = 0; i < SERVICE_COUNT; ++i) { - if (type.compare(kValidServiceTypes[i]) == 0) { - service_type_ = static_cast(i); - break; - } - } - - if (service_type_ == SERVICE_COUNT) { - LOG(LS_ERROR) << "Invalid service type: " << type; - return false; - } - - service_address_ = tokens[1]; - - int port; - tokens.clear(); - talk_base::tokenize(service_address_, ':', &tokens); - if (tokens.size() != kConfigTokens) { - port = kDefaultStunPort; - } else { - port = atoi(tokens[1].c_str()); - if (port <= 0 || port > 0xffff) { - LOG(LS_ERROR) << "Invalid port: " << tokens[1]; - return false; - } - } - - talk_base::SocketAddress stun_addr(tokens[0], port); - - socket_factory_.reset(new talk_base::BasicPacketSocketFactory( - media_thread_.get())); - - port_allocator_.reset(new cricket::BasicPortAllocator(network_manager_.get(), - stun_addr, talk_base::SocketAddress(), talk_base::SocketAddress(), - talk_base::SocketAddress())); - - ASSERT(port_allocator_.get() != NULL); - port_allocator_->set_flags(cricket::PORTALLOCATOR_DISABLE_STUN | - cricket::PORTALLOCATOR_DISABLE_TCP | - cricket::PORTALLOCATOR_DISABLE_RELAY); - - // create channel manager - channel_manager_.reset(new cricket::ChannelManager(media_thread_.get())); - - //start the media thread - media_thread_->SetPriority(talk_base::PRIORITY_HIGH); - media_thread_->SetName("PeerConn", this); - if (!media_thread_->Start()) { - LOG(LS_ERROR) << "Failed to start media thread"; - } else if (!channel_manager_->Init()) { - LOG(LS_ERROR) << "Failed to initialize the channel manager"; - } if (!signaling_thread_->SetName("Session Signaling Thread", this) || - !signaling_thread_->Start()) { - LOG(LS_ERROR) << "Failed to start session signaling thread"; - } else { - initialized_ = true; - } - - return initialized_; -} - -void PeerConnection::RegisterObserver(PeerConnectionObserver* observer) { - // This assert is to catch cases where two observer pointers are registered. - // We only support one and if another is to be used, the current one must be - // cleared first. - ASSERT(observer == NULL || event_callback_ == NULL); - event_callback_ = observer; -} - -bool PeerConnection::SignalingMessage(const std::string& signaling_message) { - // Deserialize signaling message - cricket::SessionDescription* incoming_sdp = NULL; - std::vector candidates; - if (!ParseJSONSignalingMessage(signaling_message, incoming_sdp, candidates)) - return false; - - bool ret = false; - if (!session_) { - // this will be incoming call - std::string sid; - talk_base::CreateRandomString(8, &sid); - std::string direction("r"); - session_ = CreateMediaSession(sid, direction); - ASSERT(session_ != NULL); - incoming_ = true; - ret = session_->OnInitiateMessage(incoming_sdp, candidates); - } else { - ret = session_->OnRemoteDescription(incoming_sdp, candidates); - } - return ret; -} - -WebRTCSessionImpl* PeerConnection::CreateMediaSession(const std::string& id, - const std::string& dir) { - WebRTCSessionImpl* session = new WebRTCSessionImpl(id, dir, - port_allocator_.get(), channel_manager_.get(), this, - signaling_thread_.get()); - if (session) { - session->SignalOnRemoveStream.connect(this, - &PeerConnection::SendRemoveSignal); - } - return session; -} - -void PeerConnection::SendRemoveSignal(WebRTCSessionImpl* session) { - if (event_callback_) { - std::string message; - if (GetJSONSignalingMessage(session->remote_description(), - session->local_candidates(), &message)) { - event_callback_->OnSignalingMessage(message); - } - } -} - -bool PeerConnection::AddStream(const std::string& stream_id, bool video) { - if (!session_) { - // if session doesn't exist then this should be an outgoing call - std::string sid; - if (!talk_base::CreateRandomString(8, &sid) || - (session_ = CreateMediaSession(sid, "s")) == NULL) { - ASSERT(false && "failed to initialize a session"); - return false; - } - } - - bool ret = false; - - if (session_->HasStream(stream_id)) { - ASSERT(false && "A stream with this name already exists"); - } else { - //TODO: we should ensure CreateVoiceChannel/CreateVideoChannel be called - // after transportchannel is ready - if (!video) { - ret = !session_->HasAudioStream() && - session_->CreateP2PTransportChannel(stream_id, video) && - session_->CreateVoiceChannel(stream_id); - } else { - ret = !session_->HasVideoStream() && - session_->CreateP2PTransportChannel(stream_id, video) && - session_->CreateVideoChannel(stream_id); - } - } - return ret; -} - -bool PeerConnection::RemoveStream(const std::string& stream_id) { - ASSERT(session_ != NULL); - return session_->RemoveStream(stream_id); -} - -void PeerConnection::OnLocalDescription( - cricket::SessionDescription* desc, - const std::vector& candidates) { - if (!desc) { - LOG(LS_ERROR) << "no local SDP "; - return; - } - - std::string message; - if (GetJSONSignalingMessage(desc, candidates, &message)) { - if (event_callback_) { - event_callback_->OnSignalingMessage(message); - } - } -} - -bool PeerConnection::SetAudioDevice(const std::string& wave_in_device, - const std::string& wave_out_device, int opts) { - return channel_manager_->SetAudioOptions(wave_in_device, wave_out_device, opts); -} - -bool PeerConnection::SetLocalVideoRenderer(cricket::VideoRenderer* renderer) { - return channel_manager_->SetLocalRenderer(renderer); -} - -bool PeerConnection::SetVideoRenderer(const std::string& stream_id, - cricket::VideoRenderer* renderer) { - ASSERT(session_ != NULL); - return session_->SetVideoRenderer(stream_id, renderer); -} - -bool PeerConnection::SetVideoRenderer(const std::string& stream_id, - ExternalRenderer* external_renderer) { - ASSERT(session_ != NULL); - return session_->SetVideoRenderer(stream_id, external_renderer); -} - -bool PeerConnection::SetVideoCapture(const std::string& cam_device) { - return channel_manager_->SetVideoOptions(cam_device); -} - -bool PeerConnection::Connect() { - return session_->Initiate(); -} - -void PeerConnection::OnAddStream(const std::string& stream_id, - int channel_id, - bool video) { - if (event_callback_) { - event_callback_->OnAddStream(stream_id, channel_id, video); - } -} - -void PeerConnection::OnRemoveStream(const std::string& stream_id, - int channel_id, - bool video) { - if (event_callback_) { - event_callback_->OnRemoveStream(stream_id, channel_id, video); - } -} - -void PeerConnection::OnRtcMediaChannelCreated(const std::string& stream_id, - int channel_id, - bool video) { - if (event_callback_) { - event_callback_->OnAddStream(stream_id, channel_id, video); - } -} - -void PeerConnection::Close() { - if (session_) - session_->RemoveAllStreams(); -} - -} // namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/peerconnection.h b/third_party_mods/libjingle/source/talk/app/peerconnection.h deleted file mode 100644 index e39c88b1c..000000000 --- a/third_party_mods/libjingle/source/talk/app/peerconnection.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// Author: mallinath@google.com (Mallinath Bareddy) - - -#ifndef TALK_APP_WEBRTC_PEERCONNECTION_H_ -#define TALK_APP_WEBRTC_PEERCONNECTION_H_ - -#include -#include "talk/base/sigslot.h" -#include "talk/base/thread.h" -#include "talk/base/scoped_ptr.h" -#include "talk/base/basicpacketsocketfactory.h" -#include "talk/session/phone/channelmanager.h" - -namespace Json { -class Value; -} - -namespace cricket { -class BasicPortAllocator; -class ChannelManager; -class VideoRenderer; -} - -#ifdef PLATFORM_CHROMIUM -class P2PSocketDispatcher; -#endif // PLATFORM_CHROMIUM - -namespace webrtc { - -class AudioDeviceModule; -class ExternalRenderer; -class WebRTCSessionImpl; - -class PeerConnectionObserver { - public: - virtual void OnError() = 0; - // serialized signaling message - virtual void OnSignalingMessage(const std::string& msg) = 0; - - // Triggered when a remote peer accepts a media connection. - virtual void OnAddStream(const std::string& stream_id, - int channel_id, - bool video) = 0; - - // Triggered when a remote peer closes a media stream. - virtual void OnRemoveStream(const std::string& stream_id, - int channel_id, - bool video) = 0; - - protected: - // Dtor protected as objects shouldn't be deleted via this interface. - ~PeerConnectionObserver() {} -}; - -class PeerConnection : public sigslot::has_slots<> { - public: - -#ifdef PLATFORM_CHROMIUM - PeerConnection(const std::string& config, - P2PSocketDispatcher* p2p_socket_dispatcher); -#else - explicit PeerConnection(const std::string& config); -#endif // PLATFORM_CHROMIUM - - ~PeerConnection(); - - bool Init(); - void RegisterObserver(PeerConnectionObserver* observer); - bool SignalingMessage(const std::string& msg); - bool AddStream(const std::string& stream_id, bool video); - bool RemoveStream(const std::string& stream_id); - bool Connect(); - void Close(); - - // TODO(ronghuawu): This section will be modified to reuse the existing libjingle APIs. - // Set Audio device - bool SetAudioDevice(const std::string& wave_in_device, - const std::string& wave_out_device, int opts); - // Set the video renderer - bool SetLocalVideoRenderer(cricket::VideoRenderer* renderer); - bool SetVideoRenderer(const std::string& stream_id, - cricket::VideoRenderer* renderer); - - bool SetVideoRenderer(const std::string& stream_id, - ExternalRenderer* external_renderer); - - // Set video capture device - // For Chromium the cam_device should use the capture session id. - // For standalone app, cam_device is the camera name. It will try to - // set the default capture device when cam_device is "". - bool SetVideoCapture(const std::string& cam_device); - - // Access to the members - const std::string& config() const { return config_; } - bool incoming() const { return incoming_; } - talk_base::Thread* media_thread() { - return media_thread_.get(); - } -#ifdef PLATFORM_CHROMIUM - P2PSocketDispatcher* p2p_socket_dispatcher() { - return p2p_socket_dispatcher_; - } -#endif // PLATFORM_CHROMIUM - - // Callbacks - void OnAddStream(const std::string& stream_id, int channel_id, bool video); - void OnRemoveStream(const std::string& stream_id, int channel_id, - bool video); - void OnLocalDescription(cricket::SessionDescription* desc, - const std::vector& candidates); - void OnRtcMediaChannelCreated(const std::string& stream_id, - int channel_id, - bool video); - private: - void SendRemoveSignal(WebRTCSessionImpl* session); - WebRTCSessionImpl* CreateMediaSession(const std::string& id, - const std::string& dir); - - std::string config_; - talk_base::scoped_ptr media_thread_; - talk_base::scoped_ptr channel_manager_; - talk_base::scoped_ptr network_manager_; - talk_base::scoped_ptr port_allocator_; - talk_base::scoped_ptr socket_factory_; - talk_base::scoped_ptr signaling_thread_; - bool initialized_; - - // NOTE: The order of the enum values must be in sync with the array - // in Init(). - enum ServiceType { - STUN, - STUNS, - TURN, - TURNS, - SERVICE_COUNT, // Also means 'invalid'. - }; - - ServiceType service_type_; - std::string service_address_; - PeerConnectionObserver* event_callback_; - WebRTCSessionImpl* session_; - bool incoming_; - -#ifdef PLATFORM_CHROMIUM - P2PSocketDispatcher* p2p_socket_dispatcher_; -#endif // PLATFORM_CHROMIUM -}; - -} - -#endif /* TALK_APP_WEBRTC_PEERCONNECTION_H_ */ diff --git a/third_party_mods/libjingle/source/talk/app/session_test/main_wnd.cc b/third_party_mods/libjingle/source/talk/app/session_test/main_wnd.cc deleted file mode 100644 index 7cf873e26..000000000 --- a/third_party_mods/libjingle/source/talk/app/session_test/main_wnd.cc +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// Author: tommi@google.com (Tomas Gunnarsson) - -#include "talk/app/session_test/main_wnd.h" - -#include "talk/base/common.h" -#include "talk/base/logging.h" - -ATOM MainWnd::wnd_class_ = 0; -const wchar_t MainWnd::kClassName[] = L"WebRTC_MainWnd"; - -// TODO(tommi): declare in header: -std::string GetDefaultServerName(); - -namespace { -void CalculateWindowSizeForText(HWND wnd, const wchar_t* text, - size_t* width, size_t* height) { - HDC dc = ::GetDC(wnd); - RECT text_rc = {0}; - ::DrawText(dc, text, -1, &text_rc, DT_CALCRECT | DT_SINGLELINE); - ::ReleaseDC(wnd, dc); - RECT client, window; - ::GetClientRect(wnd, &client); - ::GetWindowRect(wnd, &window); - - *width = text_rc.right - text_rc.left; - *width += (window.right - window.left) - - (client.right - client.left); - *height = text_rc.bottom - text_rc.top; - *height += (window.bottom - window.top) - - (client.bottom - client.top); -} - -HFONT GetDefaultFont() { - static HFONT font = reinterpret_cast(GetStockObject(DEFAULT_GUI_FONT)); - return font; -} - -std::string GetWindowText(HWND wnd) { - char text[MAX_PATH] = {0}; - ::GetWindowTextA(wnd, &text[0], ARRAYSIZE(text)); - return text; -} - -void AddListBoxItem(HWND listbox, const std::string& str, LPARAM item_data) { - LRESULT index = ::SendMessageA(listbox, LB_ADDSTRING, 0, - reinterpret_cast(str.c_str())); - ::SendMessageA(listbox, LB_SETITEMDATA, index, item_data); -} - -} // namespace - -MainWnd::MainWnd() - : ui_(CONNECT_TO_SERVER), wnd_(NULL), edit1_(NULL), edit2_(NULL), - label1_(NULL), label2_(NULL), button_(NULL), listbox_(NULL), - destroyed_(false), callback_(NULL), nested_msg_(NULL) { -} - -MainWnd::~MainWnd() { - ASSERT(!IsWindow()); -} - -bool MainWnd::Create() { - ASSERT(wnd_ == NULL); - if (!RegisterWindowClass()) - return false; - - wnd_ = ::CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, kClassName, L"WebRTC", - WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - NULL, NULL, GetModuleHandle(NULL), this); - - ::SendMessage(wnd_, WM_SETFONT, reinterpret_cast(GetDefaultFont()), - TRUE); - - CreateChildWindows(); - SwitchToConnectUI(); - - return wnd_ != NULL; -} - -bool MainWnd::Destroy() { - BOOL ret = FALSE; - if (IsWindow()) { - ret = ::DestroyWindow(wnd_); - } - - return ret != FALSE; -} - -void MainWnd::RegisterObserver(MainWndCallback* callback) { - callback_ = callback; -} - -bool MainWnd::IsWindow() const { - return wnd_ && ::IsWindow(wnd_) != FALSE; -} - -bool MainWnd::PreTranslateMessage(MSG* msg) { - bool ret = false; - if (msg->message == WM_CHAR) { - if (msg->wParam == VK_TAB) { - HandleTabbing(); - ret = true; - } else if (msg->wParam == VK_RETURN) { - OnDefaultAction(); - ret = true; - } else if (msg->wParam == VK_ESCAPE) { - if (callback_) { - if (ui_ == STREAMING) { - callback_->DisconnectFromCurrentPeer(); - } else { - callback_->DisconnectFromServer(); - } - } - } - } - return ret; -} - -void MainWnd::SwitchToConnectUI() { - ASSERT(IsWindow()); - LayoutPeerListUI(false); - ui_ = CONNECT_TO_SERVER; - LayoutConnectUI(true); - ::SetFocus(edit1_); -} - -void MainWnd::SwitchToPeerList(const Peers& peers) { - LayoutConnectUI(false); - - ::SendMessage(listbox_, LB_RESETCONTENT, 0, 0); - - AddListBoxItem(listbox_, "List of currently connected peers:", -1); - Peers::const_iterator i = peers.begin(); - for (; i != peers.end(); ++i) - AddListBoxItem(listbox_, i->second.c_str(), i->first); - - ui_ = LIST_PEERS; - LayoutPeerListUI(true); -} - -void MainWnd::SwitchToStreamingUI() { - LayoutConnectUI(false); - LayoutPeerListUI(false); - ui_ = STREAMING; -} - -void MainWnd::OnPaint() { - PAINTSTRUCT ps; - ::BeginPaint(handle(), &ps); - - RECT rc; - ::GetClientRect(handle(), &rc); - HBRUSH brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW)); - ::FillRect(ps.hdc, &rc, brush); - ::DeleteObject(brush); - - ::EndPaint(handle(), &ps); -} - -void MainWnd::OnDestroyed() { - PostQuitMessage(0); -} - -void MainWnd::OnDefaultAction() { - if (!callback_) - return; - if (ui_ == CONNECT_TO_SERVER) { - std::string server(GetWindowText(edit1_)); - std::string port_str(GetWindowText(edit2_)); - int port = port_str.length() ? atoi(port_str.c_str()) : 0; - callback_->StartLogin(server, port); - } else if (ui_ == LIST_PEERS) { - LRESULT sel = ::SendMessage(listbox_, LB_GETCURSEL, 0, 0); - if (sel != LB_ERR) { - LRESULT peer_id = ::SendMessage(listbox_, LB_GETITEMDATA, sel, 0); - if (peer_id != -1 && callback_) { - callback_->ConnectToPeer(peer_id); - } - } - } else { - MessageBoxA(wnd_, "OK!", "Yeah", MB_OK); - } -} - -bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result) { - switch (msg) { - case WM_ERASEBKGND: - *result = TRUE; - return true; - case WM_PAINT: - OnPaint(); - return true; - case WM_SETFOCUS: - if (ui_ == CONNECT_TO_SERVER) { - SetFocus(edit1_); - } - return true; - case WM_SIZE: - if (ui_ == CONNECT_TO_SERVER) { - LayoutConnectUI(true); - } else if (ui_ == LIST_PEERS) { - LayoutPeerListUI(true); - } - break; - case WM_CTLCOLORSTATIC: - *result = reinterpret_cast(GetSysColorBrush(COLOR_WINDOW)); - return true; - case WM_COMMAND: - if (button_ == reinterpret_cast(lp)) { - if (BN_CLICKED == HIWORD(wp)) - OnDefaultAction(); - } else if (listbox_ == reinterpret_cast(lp)) { - if (LBN_DBLCLK == HIWORD(wp)) { - OnDefaultAction(); - } - } - return true; - } - return false; -} - -// static -LRESULT CALLBACK MainWnd::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { - MainWnd* me = reinterpret_cast( - ::GetWindowLongPtr(hwnd, GWL_USERDATA)); - if (!me && WM_CREATE == msg) { - CREATESTRUCT* cs = reinterpret_cast(lp); - me = reinterpret_cast(cs->lpCreateParams); - me->wnd_ = hwnd; - ::SetWindowLongPtr(hwnd, GWL_USERDATA, reinterpret_cast(me)); - } - - LRESULT result = 0; - if (me) { - void* prev_nested_msg = me->nested_msg_; - me->nested_msg_ = &msg; - - bool handled = me->OnMessage(msg, wp, lp, &result); - if (WM_NCDESTROY == msg) { - me->destroyed_ = true; - } else if (!handled) { - result = ::DefWindowProc(hwnd, msg, wp, lp); - } - - if (me->destroyed_ && prev_nested_msg == NULL) { - me->OnDestroyed(); - me->wnd_ = NULL; - me->destroyed_ = false; - } - - me->nested_msg_ = prev_nested_msg; - } else { - result = ::DefWindowProc(hwnd, msg, wp, lp); - } - - return result; -} - -// static -bool MainWnd::RegisterWindowClass() { - if (wnd_class_) - return true; - - WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; - wcex.style = CS_DBLCLKS; - wcex.hInstance = GetModuleHandle(NULL); - wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); - wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW); - wcex.lpfnWndProc = &WndProc; - wcex.lpszClassName = kClassName; - wnd_class_ = ::RegisterClassEx(&wcex); - ASSERT(wnd_class_); - return wnd_class_ != 0; -} - -void MainWnd::CreateChildWindow(HWND* wnd, MainWnd::ChildWindowID id, - const wchar_t* class_name, DWORD control_style, - DWORD ex_style) { - if (::IsWindow(*wnd)) - return; - - // Child windows are invisible at first, and shown after being resized. - DWORD style = WS_CHILD | control_style; - *wnd = ::CreateWindowEx(ex_style, class_name, L"", style, - 100, 100, 100, 100, wnd_, - reinterpret_cast(id), - GetModuleHandle(NULL), NULL); - ASSERT(::IsWindow(*wnd)); - ::SendMessage(*wnd, WM_SETFONT, reinterpret_cast(GetDefaultFont()), - TRUE); -} - -void MainWnd::CreateChildWindows() { - // Create the child windows in tab order. - CreateChildWindow(&label1_, LABEL1_ID, L"Static", ES_CENTER | ES_READONLY, 0); - CreateChildWindow(&edit1_, EDIT_ID, L"Edit", - ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, WS_EX_CLIENTEDGE); - CreateChildWindow(&label2_, LABEL2_ID, L"Static", ES_CENTER | ES_READONLY, 0); - CreateChildWindow(&edit2_, EDIT_ID, L"Edit", - ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, WS_EX_CLIENTEDGE); - CreateChildWindow(&button_, BUTTON_ID, L"Button", BS_CENTER | WS_TABSTOP, 0); - - CreateChildWindow(&listbox_, LISTBOX_ID, L"ListBox", - LBS_HASSTRINGS | LBS_NOTIFY, WS_EX_CLIENTEDGE); - - ::SetWindowTextA(edit1_, GetDefaultServerName().c_str()); - ::SetWindowTextA(edit2_, "8888"); -} - -void MainWnd::LayoutConnectUI(bool show) { - struct Windows { - HWND wnd; - const wchar_t* text; - size_t width; - size_t height; - } windows[] = { - { label1_, L"Server" }, - { edit1_, L"XXXyyyYYYgggXXXyyyYYYggg" }, - { label2_, L":" }, - { edit2_, L"XyXyX" }, - { button_, L"Connect" }, - }; - - if (show) { - const size_t kSeparator = 5; - size_t total_width = (ARRAYSIZE(windows) - 1) * kSeparator; - - for (size_t i = 0; i < ARRAYSIZE(windows); ++i) { - CalculateWindowSizeForText(windows[i].wnd, windows[i].text, - &windows[i].width, &windows[i].height); - total_width += windows[i].width; - } - - RECT rc; - ::GetClientRect(wnd_, &rc); - size_t x = (rc.right / 2) - (total_width / 2); - size_t y = rc.bottom / 2; - for (size_t i = 0; i < ARRAYSIZE(windows); ++i) { - size_t top = y - (windows[i].height / 2); - ::MoveWindow(windows[i].wnd, x, top, windows[i].width, windows[i].height, - TRUE); - x += kSeparator + windows[i].width; - if (windows[i].text[0] != 'X') - ::SetWindowText(windows[i].wnd, windows[i].text); - ::ShowWindow(windows[i].wnd, SW_SHOWNA); - } - } else { - for (size_t i = 0; i < ARRAYSIZE(windows); ++i) { - ::ShowWindow(windows[i].wnd, SW_HIDE); - } - } -} - -void MainWnd::LayoutPeerListUI(bool show) { - if (show) { - RECT rc; - ::GetClientRect(wnd_, &rc); - ::MoveWindow(listbox_, 0, 0, rc.right, rc.bottom, TRUE); - ::ShowWindow(listbox_, SW_SHOWNA); - } else { - ::ShowWindow(listbox_, SW_HIDE); - } -} - -void MainWnd::HandleTabbing() { - bool shift = ((::GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0); - UINT next_cmd = shift ? GW_HWNDPREV : GW_HWNDNEXT; - UINT loop_around_cmd = shift ? GW_HWNDLAST : GW_HWNDFIRST; - HWND focus = GetFocus(), next; - do { - next = ::GetWindow(focus, next_cmd); - if (IsWindowVisible(next) && - (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP)) { - break; - } - - if (!next) { - next = ::GetWindow(focus, loop_around_cmd); - if (IsWindowVisible(next) && - (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP)) { - break; - } - } - focus = next; - } while (true); - ::SetFocus(next); -} diff --git a/third_party_mods/libjingle/source/talk/app/session_test/main_wnd.h b/third_party_mods/libjingle/source/talk/app/session_test/main_wnd.h deleted file mode 100644 index 18879d99b..000000000 --- a/third_party_mods/libjingle/source/talk/app/session_test/main_wnd.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// Author: tommi@google.com (Tomas Gunnarsson) - - -#ifndef TALK_APP_SESSION_TEST_MAIN_WND_H_ -#define TALK_APP_SESSION_TEST_MAIN_WND_H_ -#pragma once - -#include "talk/base/win32.h" - -#include - -// TODO(tommi): Move to same header as PeerConnectionClient. -typedef std::map Peers; - - -class MainWndCallback { - public: - virtual void StartLogin(const std::string& server, int port) = 0; - virtual void DisconnectFromServer() = 0; - virtual void ConnectToPeer(int peer_id) = 0; - virtual void DisconnectFromCurrentPeer() = 0; -}; - -class MainWnd { - public: - static const wchar_t kClassName[]; - - enum UI { - CONNECT_TO_SERVER, - LIST_PEERS, - STREAMING, - }; - - MainWnd(); - ~MainWnd(); - - bool Create(); - bool Destroy(); - bool IsWindow() const; - - void RegisterObserver(MainWndCallback* callback); - - bool PreTranslateMessage(MSG* msg); - - void SwitchToConnectUI(); - void SwitchToPeerList(const Peers& peers); - void SwitchToStreamingUI(); - - HWND handle() const { return wnd_; } - UI current_ui() const { return ui_; } - - protected: - enum ChildWindowID { - EDIT_ID = 1, - BUTTON_ID, - LABEL1_ID, - LABEL2_ID, - LISTBOX_ID, - }; - - void OnPaint(); - void OnDestroyed(); - - void OnDefaultAction(); - - bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result); - - static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); - static bool RegisterWindowClass(); - - void CreateChildWindow(HWND* wnd, ChildWindowID id, const wchar_t* class_name, - DWORD control_style, DWORD ex_style); - void CreateChildWindows(); - - void LayoutConnectUI(bool show); - void LayoutPeerListUI(bool show); - - void HandleTabbing(); - - private: - UI ui_; - HWND wnd_; - HWND edit1_; - HWND edit2_; - HWND label1_; - HWND label2_; - HWND button_; - HWND listbox_; - bool destroyed_; - void* nested_msg_; - MainWndCallback* callback_; - static ATOM wnd_class_; -}; - -#endif // TALK_APP_SESSION_TEST_MAIN_WND_H_ diff --git a/third_party_mods/libjingle/source/talk/app/session_test/session_test_main.cc b/third_party_mods/libjingle/source/talk/app/session_test/session_test_main.cc deleted file mode 100644 index e783bdfb7..000000000 --- a/third_party_mods/libjingle/source/talk/app/session_test/session_test_main.cc +++ /dev/null @@ -1,870 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// Author: tommi@google.com (Tomas Gunnarsson) - -// This may not look like much but it has already uncovered several issues. -// In the future this will be a p2p reference app for the webrtc API along -// with a separate simple server implementation. - -#include "talk/base/win32.h" // Must be first - -#include - -#include "talk/base/scoped_ptr.h" -#include "talk/base/win32socketinit.cc" -#include "talk/base/win32socketserver.h" // For Win32Socket -#include "talk/base/win32socketserver.cc" // For Win32Socket - -#include "modules/audio_device/main/interface/audio_device.h" -#include "modules/video_capture/main/interface/video_capture.h" -#include "system_wrappers/source/trace_impl.h" -#include "talk/app/peerconnection.h" -#include "talk/app/session_test/main_wnd.h" -#include "talk/base/logging.h" -#include "talk/session/phone/videorendererfactory.h" - -static const char kAudioLabel[] = "audio_label"; -static const char kVideoLabel[] = "video_label"; -const unsigned short kDefaultServerPort = 8888; - -using talk_base::scoped_ptr; -using webrtc::AudioDeviceModule; -using webrtc::PeerConnection; -using webrtc::PeerConnectionObserver; - -std::string GetEnvVarOrDefault(const char* env_var_name, - const char* default_value) { - std::string value; - const char* env_var = getenv(env_var_name); - if (env_var) - value = env_var; - - if (value.empty()) - value = default_value; - - return value; -} - -std::string GetPeerConnectionString() { - return GetEnvVarOrDefault("WEBRTC_CONNECT", "STUN stun.l.google.com:19302"); -} - -std::string GetDefaultServerName() { - return GetEnvVarOrDefault("WEBRTC_SERVER", "localhost"); -} - -std::string GetPeerName() { - char computer_name[MAX_PATH] = {0}, user_name[MAX_PATH] = {0}; - DWORD size = ARRAYSIZE(computer_name); - ::GetComputerNameA(computer_name, &size); - size = ARRAYSIZE(user_name); - ::GetUserNameA(user_name, &size); - std::string ret(user_name); - ret += '@'; - ret += computer_name; - return ret; -} - -struct PeerConnectionClientObserver { - virtual void OnSignedIn() = 0; // Called when we're "logged" on. - virtual void OnDisconnected() = 0; - virtual void OnPeerConnected(int id, const std::string& name) = 0; - virtual void OnPeerDisconnected(int id, const std::string& name) = 0; - virtual void OnMessageFromPeer(int peer_id, const std::string& message) = 0; -}; - -class PeerConnectionClient : public sigslot::has_slots<> { - public: - enum State { - NOT_CONNECTED, - SIGNING_IN, - CONNECTED, - SIGNING_OUT_WAITING, - SIGNING_OUT, - }; - - PeerConnectionClient() : callback_(NULL), my_id_(-1), state_(NOT_CONNECTED) { - control_socket_.SignalCloseEvent.connect(this, - &PeerConnectionClient::OnClose); - hanging_get_.SignalCloseEvent.connect(this, - &PeerConnectionClient::OnClose); - control_socket_.SignalConnectEvent.connect(this, - &PeerConnectionClient::OnConnect); - hanging_get_.SignalConnectEvent.connect(this, - &PeerConnectionClient::OnHangingGetConnect); - control_socket_.SignalReadEvent.connect(this, - &PeerConnectionClient::OnRead); - hanging_get_.SignalReadEvent.connect(this, - &PeerConnectionClient::OnHangingGetRead); - } - - ~PeerConnectionClient() { - } - - int id() const { - return my_id_; - } - - bool is_connected() const { - return my_id_ != -1; - } - - const Peers& peers() const { - return peers_; - } - - void RegisterObserver(PeerConnectionClientObserver* callback) { - ASSERT(!callback_); - callback_ = callback; - } - - bool Connect(const std::string& server, int port, - const std::string& client_name) { - ASSERT(!server.empty()); - ASSERT(!client_name.empty()); - ASSERT(state_ == NOT_CONNECTED); - - if (server.empty() || client_name.empty()) - return false; - - if (port <= 0) - port = kDefaultServerPort; - - server_address_.SetIP(server); - server_address_.SetPort(port); - - if (server_address_.IsUnresolved()) { - hostent* h = gethostbyname(server_address_.IPAsString().c_str()); - if (!h) { - LOG(LS_ERROR) << "Failed to resolve host name: " - << server_address_.IPAsString(); - return false; - } else { - server_address_.SetResolvedIP( - ntohl(*reinterpret_cast(h->h_addr_list[0]))); - } - } - - char buffer[1024]; - wsprintfA(buffer, "GET /sign_in?%s HTTP/1.0\r\n\r\n", client_name.c_str()); - onconnect_data_ = buffer; - - bool ret = ConnectControlSocket(); - if (ret) - state_ = SIGNING_IN; - - return ret; - } - - bool SendToPeer(int peer_id, const std::string& message) { - if (state_ != CONNECTED) - return false; - - ASSERT(is_connected()); - ASSERT(control_socket_.GetState() == talk_base::Socket::CS_CLOSED); - if (!is_connected() || peer_id == -1) - return false; - - char headers[1024]; - wsprintfA(headers, "POST /message?peer_id=%i&to=%i HTTP/1.0\r\n" - "Content-Length: %i\r\n" - "Content-Type: text/plain\r\n" - "\r\n", - my_id_, peer_id, message.length()); - onconnect_data_ = headers; - onconnect_data_ += message; - return ConnectControlSocket(); - } - - bool SignOut() { - if (state_ == NOT_CONNECTED || state_ == SIGNING_OUT) - return true; - - if (hanging_get_.GetState() != talk_base::Socket::CS_CLOSED) - hanging_get_.Close(); - - if (control_socket_.GetState() == talk_base::Socket::CS_CLOSED) { - ASSERT(my_id_ != -1); - state_ = SIGNING_OUT; - - char buffer[1024]; - wsprintfA(buffer, "GET /sign_out?peer_id=%i HTTP/1.0\r\n\r\n", my_id_); - onconnect_data_ = buffer; - return ConnectControlSocket(); - } else { - state_ = SIGNING_OUT_WAITING; - } - - return true; - } - - protected: - void Close() { - control_socket_.Close(); - hanging_get_.Close(); - onconnect_data_.clear(); - peers_.clear(); - my_id_ = -1; - state_ = NOT_CONNECTED; - } - - bool ConnectControlSocket() { - ASSERT(control_socket_.GetState() == talk_base::Socket::CS_CLOSED); - int err = control_socket_.Connect(server_address_); - if (err == SOCKET_ERROR) { - Close(); - return false; - } - return true; - } - - void OnConnect(talk_base::AsyncSocket* socket) { - ASSERT(!onconnect_data_.empty()); - int sent = socket->Send(onconnect_data_.c_str(), onconnect_data_.length()); - ASSERT(sent == onconnect_data_.length()); - onconnect_data_.clear(); - } - - void OnHangingGetConnect(talk_base::AsyncSocket* socket) { - char buffer[1024]; - wsprintfA(buffer, "GET /wait?peer_id=%i HTTP/1.0\r\n\r\n", my_id_); - int len = lstrlenA(buffer); - int sent = socket->Send(buffer, len); - ASSERT(sent == len); - } - - // Quick and dirty support for parsing HTTP header values. - bool GetHeaderValue(const std::string& data, size_t eoh, - const char* header_pattern, size_t* value) { - ASSERT(value); - size_t found = data.find(header_pattern); - if (found != std::string::npos && found < eoh) { - *value = atoi(&data[found + lstrlenA(header_pattern)]); - return true; - } - return false; - } - - bool GetHeaderValue(const std::string& data, size_t eoh, - const char* header_pattern, std::string* value) { - ASSERT(value); - size_t found = data.find(header_pattern); - if (found != std::string::npos && found < eoh) { - size_t begin = found + lstrlenA(header_pattern); - size_t end = data.find("\r\n", begin); - if (end == std::string::npos) - end = eoh; - value->assign(data.substr(begin, end - begin)); - return true; - } - return false; - } - - // Returns true if the whole response has been read. - bool ReadIntoBuffer(talk_base::AsyncSocket* socket, std::string* data, - size_t* content_length) { - LOG(INFO) << __FUNCTION__; - - char buffer[0xffff]; - do { - int bytes = socket->Recv(buffer, sizeof(buffer)); - if (bytes <= 0) - break; - data->append(buffer, bytes); - } while (true); - - bool ret = false; - size_t i = data->find("\r\n\r\n"); - if (i != std::string::npos) { - LOG(INFO) << "Headers received"; - const char kContentLengthHeader[] = "\r\nContent-Length: "; - if (GetHeaderValue(*data, i, "\r\nContent-Length: ", content_length)) { - LOG(INFO) << "Expecting " << *content_length << " bytes."; - size_t total_response_size = (i + 4) + *content_length; - if (data->length() >= total_response_size) { - ret = true; - std::string should_close; - const char kConnection[] = "\r\nConnection: "; - if (GetHeaderValue(*data, i, kConnection, &should_close) && - should_close.compare("close") == 0) { - socket->Close(); - } - } else { - // We haven't received everything. Just continue to accept data. - } - } else { - LOG(LS_ERROR) << "No content length field specified by the server."; - } - } - return ret; - } - - void OnRead(talk_base::AsyncSocket* socket) { - LOG(INFO) << __FUNCTION__; - size_t content_length = 0; - if (ReadIntoBuffer(socket, &control_data_, &content_length)) { - size_t peer_id = 0, eoh = 0; - bool ok = ParseServerResponse(control_data_, content_length, &peer_id, - &eoh); - if (ok) { - if (my_id_ == -1) { - // First response. Let's store our server assigned ID. - ASSERT(state_ == SIGNING_IN); - my_id_ = peer_id; - ASSERT(my_id_ != -1); - - // The body of the response will be a list of already connected peers. - if (content_length) { - size_t pos = eoh + 4; - while (pos < control_data_.size()) { - size_t eol = control_data_.find('\n', pos); - if (eol == std::string::npos) - break; - int id = 0; - std::string name; - bool connected; - if (ParseEntry(control_data_.substr(pos, eol - pos), &name, &id, - &connected) && id != my_id_) { - peers_[id] = name; - callback_->OnPeerConnected(id, name); - } - pos = eol + 1; - } - } - ASSERT(is_connected()); - callback_->OnSignedIn(); - } else if (state_ == SIGNING_OUT) { - Close(); - callback_->OnDisconnected(); - } else if (state_ == SIGNING_OUT_WAITING) { - SignOut(); - } - } - - control_data_.clear(); - - if (state_ == SIGNING_IN) { - ASSERT(hanging_get_.GetState() == talk_base::Socket::CS_CLOSED); - state_ = CONNECTED; - hanging_get_.Connect(server_address_); - } - } - } - - void OnHangingGetRead(talk_base::AsyncSocket* socket) { - LOG(INFO) << __FUNCTION__; - size_t content_length = 0; - if (ReadIntoBuffer(socket, ¬ification_data_, &content_length)) { - size_t peer_id = 0, eoh = 0; - bool ok = ParseServerResponse(notification_data_, content_length, - &peer_id, &eoh); - - if (ok) { - // Store the position where the body begins. - size_t pos = eoh + 4; - - if (my_id_ == peer_id) { - // A notification about a new member or a member that just - // disconnected. - int id = 0; - std::string name; - bool connected = false; - if (ParseEntry(notification_data_.substr(pos), &name, &id, - &connected)) { - if (connected) { - peers_[id] = name; - callback_->OnPeerConnected(id, name); - } else { - peers_.erase(id); - callback_->OnPeerDisconnected(id, name); - } - } - } else { - callback_->OnMessageFromPeer(peer_id, - notification_data_.substr(pos)); - } - } - - notification_data_.clear(); - } - - if (hanging_get_.GetState() == talk_base::Socket::CS_CLOSED && - state_ == CONNECTED) { - hanging_get_.Connect(server_address_); - } - } - - // Parses a single line entry in the form ",," - bool ParseEntry(const std::string& entry, std::string* name, int* id, - bool* connected) { - ASSERT(name); - ASSERT(id); - ASSERT(connected); - ASSERT(entry.length()); - - *connected = false; - size_t separator = entry.find(','); - if (separator != std::string::npos) { - *id = atoi(&entry[separator + 1]); - name->assign(entry.substr(0, separator)); - separator = entry.find(',', separator + 1); - if (separator != std::string::npos) { - *connected = atoi(&entry[separator + 1]) ? true : false; - } - } - return !name->empty(); - } - - int GetResponseStatus(const std::string& response) { - int status = -1; - size_t pos = response.find(' '); - if (pos != std::string::npos) - status = atoi(&response[pos + 1]); - return status; - } - - bool ParseServerResponse(const std::string& response, size_t content_length, - size_t* peer_id, size_t* eoh) { - LOG(INFO) << response; - - int status = GetResponseStatus(response.c_str()); - if (status != 200) { - LOG(LS_ERROR) << "Received error from server"; - Close(); - callback_->OnDisconnected(); - return false; - } - - *eoh = response.find("\r\n\r\n"); - ASSERT(*eoh != std::string::npos); - if (*eoh == std::string::npos) - return false; - - *peer_id = -1; - - // See comment in peer_channel.cc for why we use the Pragma header and - // not e.g. "X-Peer-Id". - GetHeaderValue(response, *eoh, "\r\nPragma: ", peer_id); - - return true; - } - - void OnClose(talk_base::AsyncSocket* socket, int err) { - LOG(INFO) << __FUNCTION__; - - socket->Close(); - - if (err != WSAECONNREFUSED) { - if (socket == &hanging_get_) { - if (state_ == CONNECTED) { - LOG(INFO) << "Issuing a new hanging get"; - hanging_get_.Close(); - hanging_get_.Connect(server_address_); - } - } - } else { - // Failed to connect to the server. - Close(); - callback_->OnDisconnected(); - } - } - - PeerConnectionClientObserver* callback_; - talk_base::SocketAddress server_address_; - talk_base::Win32Socket control_socket_; - talk_base::Win32Socket hanging_get_; - std::string onconnect_data_; - std::string control_data_; - std::string notification_data_; - Peers peers_; - State state_; - int my_id_; -}; - -class ConnectionObserver - : public PeerConnectionObserver, - public PeerConnectionClientObserver, - public MainWndCallback, - public talk_base::Win32Window { - public: - enum WindowMessages { - MEDIA_CHANNELS_INITIALIZED = WM_APP + 1, - PEER_CONNECTION_CLOSED, - SEND_MESSAGE_TO_PEER, - }; - - enum HandshakeState { - NONE, - INITIATOR, - ANSWER_RECEIVED, - OFFER_RECEIVED, - QUIT_SENT, - }; - - ConnectionObserver(PeerConnectionClient* client, - MainWnd* main_wnd) - : handshake_(NONE), - waiting_for_audio_(false), - waiting_for_video_(false), - peer_id_(-1), - video_channel_(-1), - audio_channel_(-1), - client_(client), - main_wnd_(main_wnd) { - // Create a window for posting notifications back to from other threads. - bool ok = Create(HWND_MESSAGE, L"ConnectionObserver", 0, 0, 0, 0, 0, 0); - ASSERT(ok); - client_->RegisterObserver(this); - main_wnd->RegisterObserver(this); - } - - ~ConnectionObserver() { - ASSERT(peer_connection_.get() == NULL); - Destroy(); - DeletePeerConnection(); - } - - bool has_video() const { - return video_channel_ != -1; - } - - bool has_audio() const { - return audio_channel_ != -1; - } - - bool connection_active() const { - return peer_connection_.get() != NULL; - } - - void Close() { - if (peer_connection_.get()) { - peer_connection_->Close(); - } else { - client_->SignOut(); - } - } - - protected: - bool InitializePeerConnection() { - ASSERT(peer_connection_.get() == NULL); - peer_connection_.reset(new PeerConnection(GetPeerConnectionString())); - peer_connection_->RegisterObserver(this); - if (!peer_connection_->Init()) { - DeletePeerConnection(); - } else { - bool audio = peer_connection_->SetAudioDevice("", "", 0); - LOG(INFO) << "SetAudioDevice " << (audio ? "succeeded." : "failed."); - } - return peer_connection_.get() != NULL; - } - - void DeletePeerConnection() { - peer_connection_.reset(); - handshake_ = NONE; - } - - void StartCaptureDevice() { - ASSERT(peer_connection_.get()); - if (main_wnd_->IsWindow()) { - main_wnd_->SwitchToStreamingUI(); - - if (peer_connection_->SetVideoCapture("")) { - if (!local_renderer_.get()) { - local_renderer_.reset( - cricket::VideoRendererFactory::CreateGuiVideoRenderer(176, 144)); - } - peer_connection_->SetLocalVideoRenderer(local_renderer_.get()); - } else { - ASSERT(false); - } - } - } - - // - // PeerConnectionObserver implementation. - // - - virtual void OnError() { - LOG(INFO) << __FUNCTION__; - ASSERT(false); - } - - virtual void OnSignalingMessage(const std::string& msg) { - LOG(INFO) << __FUNCTION__; - - bool shutting_down = (video_channel_ == -1 && audio_channel_ == -1); - - if (handshake_ == OFFER_RECEIVED && !shutting_down) - StartCaptureDevice(); - - // Send our answer/offer/shutting down message. - // If we're the initiator, this will be our offer. If we just received - // an offer, this will be an answer. If PeerConnection::Close has been - // called, then this is our signal to the other end that we're shutting - // down. - if (handshake_ != QUIT_SENT) { - SendMessage(handle(), SEND_MESSAGE_TO_PEER, 0, - reinterpret_cast(&msg)); - } - - if (shutting_down) { - handshake_ = QUIT_SENT; - PostMessage(handle(), PEER_CONNECTION_CLOSED, 0, 0); - } - } - - // Called when a remote stream is added - virtual void OnAddStream(const std::string& stream_id, int channel_id, - bool video) { - LOG(INFO) << __FUNCTION__ << " " << stream_id; - bool send_notification = (waiting_for_video_ || waiting_for_audio_); - if (video) { - ASSERT(video_channel_ == -1); - video_channel_ = channel_id; - waiting_for_video_ = false; - LOG(INFO) << "Setting video renderer for channel: " << channel_id; - if (!remote_renderer_.get()) { - remote_renderer_.reset( - cricket::VideoRendererFactory::CreateGuiVideoRenderer(352, 288)); - } - bool ok = peer_connection_->SetVideoRenderer(stream_id, - remote_renderer_.get()); - ASSERT(ok); - } else { - ASSERT(audio_channel_ == -1); - audio_channel_ = channel_id; - waiting_for_audio_ = false; - } - - if (send_notification && !waiting_for_audio_ && !waiting_for_video_) - PostMessage(handle(), MEDIA_CHANNELS_INITIALIZED, 0, 0); - } - - virtual void OnRemoveStream(const std::string& stream_id, - int channel_id, - bool video) { - LOG(INFO) << __FUNCTION__; - if (video) { - ASSERT(channel_id == video_channel_); - video_channel_ = -1; - } else { - ASSERT(channel_id == audio_channel_); - audio_channel_ = -1; - } - } - - // - // PeerConnectionClientObserver implementation. - // - - virtual void OnSignedIn() { - LOG(INFO) << __FUNCTION__; - main_wnd_->SwitchToPeerList(client_->peers()); - } - - virtual void OnDisconnected() { - LOG(INFO) << __FUNCTION__; - if (peer_connection_.get()) { - peer_connection_->Close(); - } else if (main_wnd_->IsWindow()) { - main_wnd_->SwitchToConnectUI(); - } - } - - virtual void OnPeerConnected(int id, const std::string& name) { - LOG(INFO) << __FUNCTION__; - // Refresh the list if we're showing it. - if (main_wnd_->current_ui() == MainWnd::LIST_PEERS) - main_wnd_->SwitchToPeerList(client_->peers()); - } - - virtual void OnPeerDisconnected(int id, const std::string& name) { - LOG(INFO) << __FUNCTION__; - if (id == peer_id_) { - LOG(INFO) << "Our peer disconnected"; - peer_id_ = -1; - // TODO: Somehow make sure that Close has been called? - if (peer_connection_.get()) - peer_connection_->Close(); - } - - // Refresh the list if we're showing it. - if (main_wnd_->current_ui() == MainWnd::LIST_PEERS) - main_wnd_->SwitchToPeerList(client_->peers()); - } - - virtual void OnMessageFromPeer(int peer_id, const std::string& message) { - ASSERT(peer_id_ == peer_id || peer_id_ == -1); - - if (handshake_ == NONE) { - handshake_ = OFFER_RECEIVED; - peer_id_ = peer_id; - if (!peer_connection_.get()) { - // Got an offer. Give it to the PeerConnection instance. - // Once processed, we will get a callback to OnSignalingMessage with - // our 'answer' which we'll send to the peer. - LOG(INFO) << "Got an offer from our peer: " << peer_id; - if (!InitializePeerConnection()) { - LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance"; - client_->SignOut(); - return; - } - } - } else if (handshake_ == INITIATOR) { - LOG(INFO) << "Remote peer sent us an answer"; - handshake_ = ANSWER_RECEIVED; - } else { - LOG(INFO) << "Remote peer is disconnecting"; - handshake_ = QUIT_SENT; - } - - peer_connection_->SignalingMessage(message); - - if (handshake_ == QUIT_SENT) { - DisconnectFromCurrentPeer(); - } - } - - // - // MainWndCallback implementation. - // - virtual void StartLogin(const std::string& server, int port) { - ASSERT(!client_->is_connected()); - if (!client_->Connect(server, port, GetPeerName())) { - MessageBoxA(main_wnd_->handle(), - ("Failed to connect to " + server).c_str(), - "Error", MB_OK | MB_ICONERROR); - } - } - - virtual void DisconnectFromServer() { - if (!client_->is_connected()) - return; - client_->SignOut(); - } - - virtual void ConnectToPeer(int peer_id) { - ASSERT(peer_id_ == -1); - ASSERT(peer_id != -1); - ASSERT(handshake_ == NONE); - - if (handshake_ != NONE) - return; - - if (InitializePeerConnection()) { - peer_id_ = peer_id; - waiting_for_video_ = peer_connection_->AddStream(kVideoLabel, true); - waiting_for_audio_ = peer_connection_->AddStream(kAudioLabel, false); - if (waiting_for_video_ || waiting_for_audio_) - handshake_ = INITIATOR; - ASSERT(waiting_for_video_ || waiting_for_audio_); - } - - if (handshake_ == NONE) { - ::MessageBoxA(main_wnd_->handle(), "Failed to initialize PeerConnection", - "Error", MB_OK | MB_ICONERROR); - } - } - - virtual void DisconnectFromCurrentPeer() { - if (peer_connection_.get()) - peer_connection_->Close(); - } - - // - // Win32Window implementation. - // - - virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result) { - bool ret = true; - if (msg == MEDIA_CHANNELS_INITIALIZED) { - ASSERT(handshake_ == INITIATOR); - bool ok = peer_connection_->Connect(); - ASSERT(ok); - StartCaptureDevice(); - // When we get an OnSignalingMessage notification, we'll send our - // json encoded signaling message to the peer, which is the first step - // of establishing a connection. - } else if (msg == PEER_CONNECTION_CLOSED) { - LOG(INFO) << "PEER_CONNECTION_CLOSED"; - DeletePeerConnection(); - ::InvalidateRect(main_wnd_->handle(), NULL, TRUE); - waiting_for_audio_ = false; - waiting_for_video_ = false; - peer_id_ = -1; - ASSERT(video_channel_ == -1); - ASSERT(audio_channel_ == -1); - if (main_wnd_->IsWindow()) { - if (client_->is_connected()) { - main_wnd_->SwitchToPeerList(client_->peers()); - } else { - main_wnd_->SwitchToConnectUI(); - } - } else { - DisconnectFromServer(); - } - } else if (msg == SEND_MESSAGE_TO_PEER) { - bool ok = client_->SendToPeer(peer_id_, - *reinterpret_cast(lp)); - if (!ok) { - LOG(LS_ERROR) << "SendToPeer failed"; - DisconnectFromServer(); - } - } else { - ret = false; - } - - return ret; - } - - protected: - HandshakeState handshake_; - bool waiting_for_audio_; - bool waiting_for_video_; - int peer_id_; - scoped_ptr peer_connection_; - PeerConnectionClient* client_; - MainWnd* main_wnd_; - int video_channel_; - int audio_channel_; - scoped_ptr local_renderer_; - scoped_ptr remote_renderer_; -}; - -int PASCAL wWinMain(HINSTANCE instance, HINSTANCE prev_instance, - wchar_t* cmd_line, int cmd_show) { - talk_base::EnsureWinsockInit(); - - webrtc::Trace::CreateTrace(); - webrtc::Trace::SetTraceFile("session_test_trace.txt"); - webrtc::Trace::SetLevelFilter(webrtc::kTraceWarning); - - MainWnd wnd; - if (!wnd.Create()) { - ASSERT(false); - return -1; - } - - PeerConnectionClient client; - ConnectionObserver observer(&client, &wnd); - - // Main loop. - MSG msg; - BOOL gm; - while ((gm = ::GetMessage(&msg, NULL, 0, 0)) && gm != -1) { - if (!wnd.PreTranslateMessage(&msg)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - } - - if (observer.connection_active() || client.is_connected()) { - observer.Close(); - while ((observer.connection_active() || client.is_connected()) && - (gm = ::GetMessage(&msg, NULL, 0, 0)) && gm != -1) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - } - - return 0; -} diff --git a/third_party_mods/libjingle/source/talk/app/videomediaengine.cc b/third_party_mods/libjingle/source/talk/app/videomediaengine.cc deleted file mode 100644 index 95387bf10..000000000 --- a/third_party_mods/libjingle/source/talk/app/videomediaengine.cc +++ /dev/null @@ -1,972 +0,0 @@ - - -#include "talk/app/videomediaengine.h" - -#include - -#ifdef PLATFORM_CHROMIUM -#include "content/renderer/video_capture_chrome.h" -#endif -#include "talk/base/buffer.h" -#include "talk/base/byteorder.h" -#include "talk/base/logging.h" -#include "talk/base/stringutils.h" -#include "talk/app/voicemediaengine.h" - -#include "modules/video_capture/main/interface/video_capture.h" -#include "vplib.h" - -#ifndef ARRAYSIZE -#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0])) -#endif - -namespace webrtc { - -static const int kDefaultLogSeverity = 3; -static const int kStartVideoBitrate = 300; -static const int kMaxVideoBitrate = 1000; - -CricketWebRTCVideoFrame::CricketWebRTCVideoFrame() { -} - -CricketWebRTCVideoFrame::~CricketWebRTCVideoFrame() { - // TODO(ronghuawu): should the CricketWebRTCVideoFrame owns the buffer? - WebRtc_UWord8* newMemory = NULL; - WebRtc_UWord32 newLength = 0; - WebRtc_UWord32 newSize = 0; - video_frame_.Swap(newMemory, newLength, newSize); -} - -void CricketWebRTCVideoFrame::Attach(unsigned char* buffer, int bufferSize, - int w, int h) { - WebRtc_UWord8* newMemory = buffer; - WebRtc_UWord32 newLength = bufferSize; - WebRtc_UWord32 newSize = bufferSize; - video_frame_.Swap(newMemory, newLength, newSize); - video_frame_.SetWidth(w); - video_frame_.SetHeight(h); -} - -size_t CricketWebRTCVideoFrame::GetWidth() const { - return video_frame_.Width(); -} -size_t CricketWebRTCVideoFrame::GetHeight() const { - return video_frame_.Height(); -} - -const uint8* CricketWebRTCVideoFrame::GetYPlane() const { - WebRtc_UWord8* buffer = video_frame_.Buffer(); - return buffer; -} - -const uint8* CricketWebRTCVideoFrame::GetUPlane() const { - WebRtc_UWord8* buffer = video_frame_.Buffer(); - if (buffer) - buffer += (video_frame_.Width() * video_frame_.Height()); - return buffer; -} - -const uint8* CricketWebRTCVideoFrame::GetVPlane() const { - WebRtc_UWord8* buffer = video_frame_.Buffer(); - if (buffer) - buffer += (video_frame_.Width() * video_frame_.Height() * 5 / 4); - return buffer; -} - -uint8* CricketWebRTCVideoFrame::GetYPlane() { - WebRtc_UWord8* buffer = video_frame_.Buffer(); - return buffer; -} - -uint8* CricketWebRTCVideoFrame::GetUPlane() { - WebRtc_UWord8* buffer = video_frame_.Buffer(); - if (buffer) - buffer += (video_frame_.Width() * video_frame_.Height()); - return buffer; -} - -uint8* CricketWebRTCVideoFrame::GetVPlane() { - WebRtc_UWord8* buffer = video_frame_.Buffer(); - if (buffer) - buffer += (video_frame_.Width() * video_frame_.Height() * 3 / 2); - return buffer; -} - -cricket::VideoFrame* CricketWebRTCVideoFrame::Copy() const { - WebRtc_UWord8* buffer = video_frame_.Buffer(); - if (buffer) { - int new_buffer_size = video_frame_.Length(); - unsigned char* new_buffer = new unsigned char[new_buffer_size]; - memcpy(new_buffer, buffer, new_buffer_size); - CricketWebRTCVideoFrame* copy = new CricketWebRTCVideoFrame(); - copy->Attach(new_buffer, new_buffer_size, - video_frame_.Width(), video_frame_.Height()); - copy->SetTimeStamp(video_frame_.TimeStamp()); - copy->SetElapsedTime(elapsed_time_); - return copy; - } - return NULL; -} - -size_t CricketWebRTCVideoFrame::CopyToBuffer( - uint8* buffer, size_t size) const { - if (!video_frame_.Buffer()) { - return 0; - } - - size_t needed = video_frame_.Length(); - if (needed <= size) { - memcpy(buffer, video_frame_.Buffer(), needed); - } - return needed; -} - -size_t CricketWebRTCVideoFrame::ConvertToRgbBuffer(uint32 to_fourcc, - uint8* buffer, - size_t size, - size_t pitch_rgb) const { - if (!video_frame_.Buffer()) { - return 0; - } - - size_t width = video_frame_.Width(); - size_t height = video_frame_.Height(); - // See http://www.virtualdub.org/blog/pivot/entry.php?id=190 for a good - // explanation of pitch and why this is the amount of space we need. - size_t needed = pitch_rgb * (height - 1) + 4 * width; - - if (needed > size) { - LOG(LS_WARNING) << "RGB buffer is not large enough"; - return needed; - } - - VideoType outgoingVideoType = kUnknown; - switch (to_fourcc) { - case cricket::FOURCC_ARGB: - outgoingVideoType = kARGB; - break; - default: - LOG(LS_WARNING) << "RGB type not supported: " << to_fourcc; - break; - } - - if (outgoingVideoType != kUnknown) - ConvertFromI420(outgoingVideoType, video_frame_.Buffer(), - width, height, buffer); - - return needed; -} - -// TODO(ronghuawu): Implement StretchToPlanes -void CricketWebRTCVideoFrame::StretchToPlanes( - uint8* y, uint8* u, uint8* v, - int32 dst_pitch_y, int32 dst_pitch_u, int32 dst_pitch_v, - size_t width, size_t height, bool interpolate, bool crop) const { -} - -size_t CricketWebRTCVideoFrame::StretchToBuffer(size_t w, size_t h, - uint8* buffer, size_t size, - bool interpolate, - bool crop) const { - if (!video_frame_.Buffer()) { - return 0; - } - - size_t needed = video_frame_.Length(); - - if (needed <= size) { - uint8* bufy = buffer; - uint8* bufu = bufy + w * h; - uint8* bufv = bufu + ((w + 1) >> 1) * ((h + 1) >> 1); - StretchToPlanes(bufy, bufu, bufv, w, (w + 1) >> 1, (w + 1) >> 1, w, h, - interpolate, crop); - } - return needed; -} - -void CricketWebRTCVideoFrame::StretchToFrame(cricket::VideoFrame *target, - bool interpolate, bool crop) const { - if (!target) return; - - StretchToPlanes(target->GetYPlane(), - target->GetUPlane(), - target->GetVPlane(), - target->GetYPitch(), - target->GetUPitch(), - target->GetVPitch(), - target->GetWidth(), - target->GetHeight(), - interpolate, crop); - target->SetElapsedTime(GetElapsedTime()); - target->SetTimeStamp(GetTimeStamp()); -} - -cricket::VideoFrame* CricketWebRTCVideoFrame::Stretch(size_t w, size_t h, - bool interpolate, bool crop) const { - // TODO(ronghuawu): implement - CricketWebRTCVideoFrame* frame = new CricketWebRTCVideoFrame(); - return frame; -} - -CricketWebRTCVideoRenderer::CricketWebRTCVideoRenderer - (cricket::VideoRenderer* renderer) - :renderer_(renderer) { -} - -CricketWebRTCVideoRenderer::~CricketWebRTCVideoRenderer() { -} - -int CricketWebRTCVideoRenderer::FrameSizeChange(unsigned int width, - unsigned int height, - unsigned int numberOfStreams) { - ASSERT(renderer_ != NULL); - width_ = width; - height_ = height; - number_of_streams_ = numberOfStreams; - return renderer_->SetSize(width_, height_, 0) ? 0 : -1; -} - -int CricketWebRTCVideoRenderer::DeliverFrame(unsigned char* buffer, - int bufferSize) { - ASSERT(renderer_ != NULL); - video_frame_.Attach(buffer, bufferSize, width_, height_); - return renderer_->RenderFrame(&video_frame_) ? 0 : -1; -} - -const RtcVideoEngine::VideoCodecPref RtcVideoEngine::kVideoCodecPrefs[] = { - {"VP8", 104, 0}, - {"H264", 105, 1} -}; - -RtcVideoEngine::RtcVideoEngine() - : video_engine_(new VideoEngineWrapper()), - capture_(NULL), - capture_id_(-1), - voice_engine_(NULL), - initialized_(false), - log_level_(kDefaultLogSeverity), - capture_started_(false){ -} - -RtcVideoEngine::RtcVideoEngine(RtcVoiceEngine* voice_engine) - : video_engine_(new VideoEngineWrapper()), - capture_(NULL), - capture_id_(-1), - voice_engine_(voice_engine), - initialized_(false), - log_level_(kDefaultLogSeverity), - capture_started_(false){ -} - -RtcVideoEngine::~RtcVideoEngine() { - LOG(LS_VERBOSE) << " RtcVideoEngine::~RtcVideoEngine"; - video_engine_->engine()->SetTraceCallback(NULL); - Terminate(); -} - -bool RtcVideoEngine::Init() { - LOG(LS_VERBOSE) << "RtcVideoEngine::Init"; - ApplyLogging(); - if (video_engine_->engine()->SetTraceCallback(this) != 0) { - LOG(LS_ERROR) << "SetTraceCallback error"; - } - - bool result = InitVideoEngine(voice_engine_); - if (result) { - LOG(LS_INFO) << "VideoEngine Init done"; - } else { - LOG(LS_ERROR) << "VideoEngine Init failed, releasing"; - Terminate(); - } - return result; -} - -bool RtcVideoEngine::InitVideoEngine(RtcVoiceEngine* voice_engine) { - LOG(LS_VERBOSE) << "RtcVideoEngine::InitVideoEngine"; - - bool ret = true; - if (video_engine_->base()->Init() != 0) { - LOG(LS_ERROR) << "VideoEngine Init method failed"; - ret = false; - } - - if (!voice_engine) { - LOG(LS_WARNING) << "NULL voice engine"; - } else if ((video_engine_->base()->SetVoiceEngine( - voice_engine->webrtc()->engine())) != 0) { - LOG(LS_WARNING) << "Failed to SetVoiceEngine"; - } - - if ((video_engine_->base()->RegisterObserver(*this)) != 0) { - LOG(LS_WARNING) << "Failed to register observer"; - } - - int ncodecs = video_engine_->codec()->NumberOfCodecs(); - for (int i = 0; i < ncodecs - 2; ++i) { - VideoCodec wcodec; - if ((video_engine_->codec()->GetCodec(i, wcodec) == 0) && - (strncmp(wcodec.plName, "I420", 4) != 0)) { //ignore I420 - cricket::VideoCodec codec(wcodec.plType, wcodec.plName, wcodec.width, - wcodec.height, wcodec.maxFramerate, i); - LOG(LS_INFO) << codec.ToString(); - video_codecs_.push_back(codec); - } - } - - std::sort(video_codecs_.begin(), video_codecs_.end(), - &cricket::VideoCodec::Preferable); - return ret; -} - -void RtcVideoEngine::PerformanceAlarm(const unsigned int cpuLoad) { - return; -} - -void RtcVideoEngine::Print(const TraceLevel level, const char *traceString, - const int length) { - return; -} - -int RtcVideoEngine::GetCodecPreference(const char* name) { - for (size_t i = 0; i < ARRAY_SIZE(kVideoCodecPrefs); ++i) { - if (strcmp(kVideoCodecPrefs[i].payload_name, name) == 0) { - return kVideoCodecPrefs[i].pref; - } - } - return -1; -} - -void RtcVideoEngine::ApplyLogging() { - int filter = 0; - switch(log_level_) { - case talk_base::LS_VERBOSE: filter |= kTraceAll; - case talk_base::LS_INFO: filter |= kTraceStateInfo; - case talk_base::LS_WARNING: filter |= kTraceWarning; - case talk_base::LS_ERROR: filter |= kTraceError | kTraceCritical; - } -} - -void RtcVideoEngine::Terminate() { - LOG(LS_INFO) << "RtcVideoEngine::Terminate"; - ReleaseCaptureDevice(); -} - -int RtcVideoEngine::GetCapabilities() { - return cricket::MediaEngine::VIDEO_RECV | cricket::MediaEngine::VIDEO_SEND; -} - -bool RtcVideoEngine::SetOptions(int options) { - return true; -} - -bool RtcVideoEngine::ReleaseCaptureDevice() { - if (capture_) { - // Stop capture - SetCapture(false); - // DisconnectCaptureDevice - RtcVideoMediaChannel* channel; - for (VideoChannels::const_iterator it = channels_.begin(); - it != channels_.end(); ++it) { - ASSERT(*it != NULL); - channel = *it; - video_engine_->capture()->DisconnectCaptureDevice(channel->video_channel()); - } - // ReleaseCaptureDevice - video_engine_->capture()->ReleaseCaptureDevice(capture_id_); - capture_id_ = -1; -#ifdef PLATFORM_CHROMIUM - VideoCaptureChrome::DestroyVideoCapture( - static_cast(capture_)); -#else - webrtc::VideoCaptureModule::Destroy(capture_); -#endif - capture_ = NULL; - } - return true; -} - -bool RtcVideoEngine::SetCaptureDevice(const cricket::Device* cam) { - ASSERT(video_engine_.get()); - ASSERT(cam != NULL); - - ReleaseCaptureDevice(); - -#ifdef PLATFORM_CHROMIUM - int cam_id = atol(cam->id.c_str()); - if (cam_id == -1) - return false; - unsigned char uniqueId[16]; - capture_ = VideoCaptureChrome::CreateVideoCapture(cam_id, uniqueId); -#else - WebRtc_UWord8 device_name[128]; - WebRtc_UWord8 device_id[260]; - VideoCaptureModule::DeviceInfo* device_info = - VideoCaptureModule::CreateDeviceInfo(0); - for (WebRtc_UWord32 i = 0; i < device_info->NumberOfDevices(); ++i) { - if (device_info->GetDeviceName(i, device_name, ARRAYSIZE(device_name), - device_id, ARRAYSIZE(device_id)) == 0) { - if ((cam->name.compare("") == 0) || - (cam->id.compare((char*) device_id) == 0)) { - capture_ = VideoCaptureModule::Create(1234, device_id); - if (capture_) { - LOG(INFO) << "Found video capture device: " << device_name; - break; - } - } - } - } - VideoCaptureModule::DestroyDeviceInfo(device_info); -#endif - - if (!capture_) - return false; - - ViECapture* vie_capture = video_engine_->capture(); - if (vie_capture->AllocateCaptureDevice(*capture_, capture_id_) == 0) { - // Connect to all the channels - RtcVideoMediaChannel* channel; - for (VideoChannels::const_iterator it = channels_.begin(); - it != channels_.end(); ++it) { - ASSERT(*it != NULL); - channel = *it; - vie_capture->ConnectCaptureDevice(capture_id_, channel->video_channel()); - } - SetCapture(true); - } else { - ASSERT(capture_id_ == -1); - } - - return (capture_id_ != -1); -} - -bool RtcVideoEngine::SetLocalRenderer(cricket::VideoRenderer* renderer) { - if (!local_renderer_.get()) { - local_renderer_.reset(new CricketWebRTCVideoRenderer(renderer)); - } else { - // Renderer already set - return true; - } - - int ret; - ret = video_engine_->render()->AddRenderer(capture_id_, - kVideoI420, - local_renderer_.get()); - if (ret != 0) - return false; - ret = video_engine_->render()->StartRender(capture_id_); - - return (ret == 0); -} - -cricket::CaptureResult RtcVideoEngine::SetCapture(bool capture) { - if (capture_started_ == capture) - return cricket::CR_SUCCESS; - - if (capture_id_ != -1) { - int ret; - if (capture) - ret = video_engine_->capture()->StartCapture(capture_id_); - else - ret = video_engine_->capture()->StopCapture(capture_id_); - if (ret == 0) { - capture_started_ = capture; - return cricket::CR_SUCCESS; - } - } - - return cricket::CR_NO_DEVICE; -} - -const std::vector& RtcVideoEngine::codecs() const { - return video_codecs_; -} - -void RtcVideoEngine::SetLogging(int min_sev, const char* filter) { - log_level_ = min_sev; - ApplyLogging(); -} - -bool RtcVideoEngine::SetDefaultEncoderConfig( - const cricket::VideoEncoderConfig& config) { - bool ret = SetDefaultCodec(config.max_codec); - if (ret) { - default_encoder_config_ = config; - } - return ret; -} - -bool RtcVideoEngine::SetDefaultCodec(const cricket::VideoCodec& codec) { - default_codec_ = codec; - return true; -} - -RtcVideoMediaChannel* RtcVideoEngine::CreateChannel( - cricket::VoiceMediaChannel* voice_channel) { - RtcVideoMediaChannel* channel = - new RtcVideoMediaChannel(this, voice_channel); - if (channel) { - if (!channel->Init()) { - delete channel; - channel = NULL; - } - } - return channel; -} - -bool RtcVideoEngine::FindCodec(const cricket::VideoCodec& codec) { - for (size_t i = 0; i < video_codecs_.size(); ++i) { - if (video_codecs_[i].Matches(codec)) { - return true; - } - } - return false; -} - -void RtcVideoEngine::ConvertToCricketVideoCodec( - const VideoCodec& in_codec, cricket::VideoCodec& out_codec) { - out_codec.id = in_codec.plType; - out_codec.name = in_codec.plName; - out_codec.width = in_codec.width; - out_codec.height = in_codec.height; - out_codec.framerate = in_codec.maxFramerate; -} - -void RtcVideoEngine::ConvertFromCricketVideoCodec( - const cricket::VideoCodec& in_codec, VideoCodec& out_codec) { - out_codec.plType = in_codec.id; - strcpy(out_codec.plName, in_codec.name.c_str()); - out_codec.width = 352; //in_codec.width; - out_codec.height = 288; //in_codec.height; - out_codec.maxFramerate = 30; //in_codec.framerate; - - if (strncmp(out_codec.plName, "VP8", 3) == 0) { - out_codec.codecType = kVideoCodecVP8; - } else if (strncmp(out_codec.plName, "H263", 4) == 0) { - out_codec.codecType = kVideoCodecH263; - } else if (strncmp(out_codec.plName, "H264", 4) == 0) { - out_codec.codecType = kVideoCodecH264; - } else if (strncmp(out_codec.plName, "I420", 4) == 0) { - out_codec.codecType = kVideoCodecI420; - } else { - LOG(LS_INFO) << "invalid codec type"; - } - - out_codec.maxBitrate = kMaxVideoBitrate; - out_codec.startBitrate = kStartVideoBitrate; - out_codec.minBitrate = kStartVideoBitrate; -} - -int RtcVideoEngine::GetLastVideoEngineError() { - return video_engine_->base()->LastError(); -} - -void RtcVideoEngine::RegisterChannel(RtcVideoMediaChannel *channel) { - talk_base::CritScope lock(&channels_cs_); - channels_.push_back(channel); -} - -void RtcVideoEngine::UnregisterChannel(RtcVideoMediaChannel *channel) { - talk_base::CritScope lock(&channels_cs_); - VideoChannels::iterator i = std::find(channels_.begin(), - channels_.end(), - channel); - if (i != channels_.end()) { - channels_.erase(i); - } -} - - - - -// RtcVideoMediaChannel - -RtcVideoMediaChannel::RtcVideoMediaChannel( - RtcVideoEngine* engine, cricket::VoiceMediaChannel* channel) - : engine_(engine), - voice_channel_(channel), - video_channel_(-1), - sending_(false), - render_started_(false) { - engine->RegisterChannel(this); -} - -bool RtcVideoMediaChannel::Init() { - bool ret = true; - if (engine_->video_engine()->base()->CreateChannel(video_channel_) != 0) { - LOG(LS_ERROR) << "ViE CreateChannel Failed!!"; - ret = false; - } - - LOG(LS_INFO) << "RtcVideoMediaChannel::Init " - << "video_channel " << video_channel_ << " created"; - //connect audio channel - if (voice_channel_) { - RtcVoiceMediaChannel* channel = - static_cast (voice_channel_); - if (engine_->video_engine()->base()->ConnectAudioChannel( - video_channel_, channel->audio_channel()) != 0) { - LOG(LS_WARNING) << "ViE ConnectAudioChannel failed" - << "A/V not synchronized"; - // Don't set ret to false; - } - } - - //Register external transport - if (engine_->video_engine()->network()->RegisterSendTransport( - video_channel_, *this) != 0) { - ret = false; - } else { - EnableRtcp(); - EnablePLI(); - } - return ret; -} - -RtcVideoMediaChannel::~RtcVideoMediaChannel() { - // Stop and remote renderer - SetRender(false); - if (engine()->video_engine()->render()->RemoveRenderer(video_channel_) == -1) { - LOG(LS_ERROR) << "Video RemoveRenderer failed for channel " - << video_channel_; - } - - // DeRegister external transport - if (engine()->video_engine()->network()->DeregisterSendTransport( - video_channel_) == -1) { - LOG(LS_ERROR) << "DeRegisterSendTransport failed for channel id " - << video_channel_; - } - - // Unregister RtcChannel with the engine. - engine()->UnregisterChannel(this); - - // Delete VideoChannel - if (engine()->video_engine()->base()->DeleteChannel(video_channel_) == -1) { - LOG(LS_ERROR) << "Video DeleteChannel failed for channel " - << video_channel_; - } -} - -bool RtcVideoMediaChannel::SetRecvCodecs( - const std::vector& codecs) { - bool ret = true; - for (std::vector::const_iterator iter = codecs.begin(); - iter != codecs.end(); ++iter) { - if (engine()->FindCodec(*iter)) { - VideoCodec wcodec; - engine()->ConvertFromCricketVideoCodec(*iter, wcodec); - if (engine()->video_engine()->codec()->SetReceiveCodec( - video_channel_, wcodec) != 0) { - LOG(LS_ERROR) << "ViE SetReceiveCodec failed" - << " VideoChannel : " << video_channel_ << " Error: " - << engine()->video_engine()->base()->LastError() - << "wcodec " << wcodec.plName; - ret = false; - } - } else { - LOG(LS_INFO) << "Unknown codec" << iter->name; - ret = false; - } - } - - // make channel ready to receive packets - if (ret) { - if (engine()->video_engine()->base()->StartReceive(video_channel_) != 0) { - LOG(LS_ERROR) << "ViE StartReceive failure"; - ret = false; - } - } - return ret; -} - -bool RtcVideoMediaChannel::SetSendCodecs( - const std::vector& codecs) { - if (sending_) { - LOG(LS_ERROR) << "channel is alredy sending"; - return false; - } - - //match with local video codec list - std::vector send_codecs; - for (std::vector::const_iterator iter = codecs.begin(); - iter != codecs.end(); ++iter) { - if (engine()->FindCodec(*iter)) { - VideoCodec wcodec; - engine()->ConvertFromCricketVideoCodec(*iter, wcodec); - send_codecs.push_back(wcodec); - } - } - - // if none matches, return with set - if (send_codecs.empty()) { - LOG(LS_ERROR) << "No matching codecs avilable"; - return false; - } - - //select the first matched codec - const VideoCodec& codec(send_codecs[0]); - send_codec_ = codec; - if (engine()->video_engine()->codec()->SetSendCodec( - video_channel_, codec) != 0) { - LOG(LS_ERROR) << "ViE SetSendCodec failed"; - return false; - } - return true; -} - -bool RtcVideoMediaChannel::SetRender(bool render) { - if (video_channel_ != -1) { - int ret = -1; - if (render == render_started_) - return true; - - if (render) { - ret = engine()->video_engine()->render()->StartRender(video_channel_); - } else { - ret = engine()->video_engine()->render()->StopRender(video_channel_); - } - - if (ret == 0) { - render_started_ = render; - return true; - } - } - return false; -} - -bool RtcVideoMediaChannel::SetSend(bool send) { - if (send == sending()) { - return true; // no action required - } - - bool ret = true; - if (send) { //enable - if (engine()->video_engine()->base()->StartSend(video_channel_) != 0) { - LOG(LS_ERROR) << "ViE StartSend failed"; - ret = false; - } - } else { // disable - if (engine()->video_engine()->base()->StopSend(video_channel_) != 0) { - LOG(LS_ERROR) << "ViE StopSend failed"; - ret = false; - } - } - if (ret) - sending_ = send; - - return ret; -} - -bool RtcVideoMediaChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) { - return false; -} - -bool RtcVideoMediaChannel::RemoveStream(uint32 ssrc) { - return false; -} - -bool RtcVideoMediaChannel::SetRenderer( - uint32 ssrc, cricket::VideoRenderer* renderer) { - if (!remote_renderer_.get()) { - remote_renderer_.reset(new CricketWebRTCVideoRenderer(renderer)); - } else { - // Renderer already set - return true; - } - - int ret; - ret = engine_->video_engine()->render()->AddRenderer(video_channel_, - kVideoI420, - remote_renderer_.get()); - if (ret != 0) - return false; - ret = engine_->video_engine()->render()->StartRender(video_channel_); - - return (ret == 0); -} - -bool RtcVideoMediaChannel::SetExternalRenderer(uint32 ssrc, void* renderer) -{ - int ret; - ret = engine_->video_engine()->render()->AddRenderer( - video_channel_, - kVideoI420, - static_cast(renderer)); - if (ret != 0) - return false; - ret = engine_->video_engine()->render()->StartRender(video_channel_); - - return (ret == 0); -} - -bool RtcVideoMediaChannel::GetStats(cricket::VideoMediaInfo* info) { - cricket::VideoSenderInfo sinfo; - memset(&sinfo, 0, sizeof(sinfo)); - - unsigned int ssrc; - if (engine_->video_engine()->rtp()->GetLocalSSRC(video_channel_, - ssrc) != 0) { - LOG(LS_ERROR) << "ViE GetLocalSSRC failed"; - return false; - } - sinfo.ssrc = ssrc; - - unsigned int cumulative_lost, extended_max, jitter; - int rtt_ms; - unsigned short fraction_lost; - - if (engine_->video_engine()->rtp()->GetSentRTCPStatistics(video_channel_, - fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms) != 0) { - LOG(LS_ERROR) << "ViE GetLocalSSRC failed"; - return false; - } - - sinfo.fraction_lost = fraction_lost; - sinfo.rtt_ms = rtt_ms; - - unsigned int bytes_sent, packets_sent, bytes_recv, packets_recv; - if (engine_->video_engine()->rtp()->GetRTPStatistics(video_channel_, - bytes_sent, packets_sent, bytes_recv, packets_recv) != 0) { - LOG(LS_ERROR) << "ViE GetRTPStatistics"; - return false; - } - sinfo.packets_sent = packets_sent; - sinfo.bytes_sent = bytes_sent; - sinfo.packets_lost = -1; - sinfo.packets_cached = -1; - - info->senders.push_back(sinfo); - - //build receiver info. - // reusing the above local variables - cricket::VideoReceiverInfo rinfo; - memset(&rinfo, 0, sizeof(rinfo)); - if (engine_->video_engine()->rtp()->GetReceivedRTCPStatistics(video_channel_, - fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms) != 0) { - LOG(LS_ERROR) << "ViE GetReceivedRTPStatistics Failed"; - return false; - } - rinfo.bytes_rcvd = bytes_recv; - rinfo.packets_rcvd = packets_recv; - rinfo.fraction_lost = fraction_lost; - - if (engine_->video_engine()->rtp()->GetRemoteSSRC(video_channel_, - ssrc) != 0) { - return false; - } - rinfo.ssrc = ssrc; - - //Get codec for wxh - info->receivers.push_back(rinfo); - return true; -} - -bool RtcVideoMediaChannel::SendIntraFrame() { - bool ret = true; - if (engine()->video_engine()->codec()->SendKeyFrame(video_channel_) != 0) { - LOG(LS_ERROR) << "ViE SendKeyFrame failed"; - ret = false; - } - - return ret; -} - -bool RtcVideoMediaChannel::RequestIntraFrame() { - //There is no API exposed to application to request a key frame - // ViE does this internally when there are errors from decoder - return true; -} - -void RtcVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { - engine()->video_engine()->network()->ReceivedRTPPacket(video_channel_, - packet->data(), - packet->length()); - -} - -void RtcVideoMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) { - engine_->video_engine()->network()->ReceivedRTCPPacket(video_channel_, - packet->data(), - packet->length()); - -} - -void RtcVideoMediaChannel::SetSendSsrc(uint32 id) { - if (!sending_){ - if (engine()->video_engine()->rtp()->SetLocalSSRC(video_channel_, id) != 0) { - LOG(LS_ERROR) << "ViE SetLocalSSRC failed"; - } - } else { - LOG(LS_ERROR) << "Channel already in send state"; - } -} - -bool RtcVideoMediaChannel::SetRtcpCName(const std::string& cname) { - if (engine()->video_engine()->rtp()->SetRTCPCName(video_channel_, - cname.c_str()) != 0) { - LOG(LS_ERROR) << "ViE SetRTCPCName failed"; - return false; - } - return true; -} - -bool RtcVideoMediaChannel::Mute(bool on) { - // stop send?? - return false; -} - -bool RtcVideoMediaChannel::SetSendBandwidth(bool autobw, int bps) { - LOG(LS_VERBOSE) << "RtcVideoMediaChanne::SetSendBandwidth"; - - VideoCodec current = send_codec_; - send_codec_.startBitrate = bps; - - if (engine()->video_engine()->codec()->SetSendCodec(video_channel_, - send_codec_) != 0) { - LOG(LS_ERROR) << "ViE SetSendCodec failed"; - if (engine()->video_engine()->codec()->SetSendCodec(video_channel_, - current) != 0) { - // should call be ended in this case? - } - return false; - } - return true; -} - -bool RtcVideoMediaChannel::SetOptions(int options) { - return true; -} - -void RtcVideoMediaChannel::EnableRtcp() { - engine()->video_engine()->rtp()->SetRTCPStatus( - video_channel_, kRtcpCompound_RFC4585); -} - -void RtcVideoMediaChannel::EnablePLI() { - engine_->video_engine()->rtp()->SetKeyFrameRequestMethod( - video_channel_, kViEKeyFrameRequestPliRtcp); -} - -void RtcVideoMediaChannel::EnableTMMBR() { - engine_->video_engine()->rtp()->SetTMMBRStatus(video_channel_, true); -} - -int RtcVideoMediaChannel::SendPacket(int channel, const void* data, int len) { - if (!network_interface_) { - return -1; - } - talk_base::Buffer packet(data, len, cricket::kMaxRtpPacketLen); - return network_interface_->SendPacket(&packet) ? len : -1; -} - -int RtcVideoMediaChannel::SendRTCPPacket(int channel, - const void* data, - int len) { - if (!network_interface_) { - return -1; - } - talk_base::Buffer packet(data, len, cricket::kMaxRtpPacketLen); - return network_interface_->SendRtcp(&packet) ? len : -1; -} - -} // namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/videomediaengine.h b/third_party_mods/libjingle/source/talk/app/videomediaengine.h deleted file mode 100644 index 4fdb62972..000000000 --- a/third_party_mods/libjingle/source/talk/app/videomediaengine.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * libjingle - * Copyright 2004--2011, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_APP_WEBRTC_VIDEOMEDIAENGINE_H_ -#define TALK_APP_WEBRTC_VIDEOMEDIAENGINE_H_ - -#include - -#include "talk/base/scoped_ptr.h" -#include "talk/session/phone/videocommon.h" -#include "talk/session/phone/codec.h" -#include "talk/session/phone/channel.h" -#include "talk/session/phone/mediaengine.h" -#include "talk/app/videoengine.h" - - -namespace cricket { -class VoiceMediaChannel; -class Device; -class VideoRenderer; -} - -namespace webrtc { -class RtcVideoMediaChannel; -class RtcVoiceEngine; -class ExternalRenderer; - -// CricketWebRTCVideoFrame only supports I420 -class CricketWebRTCVideoFrame : public cricket::VideoFrame { - public: - CricketWebRTCVideoFrame(); - ~CricketWebRTCVideoFrame(); - - void Attach(unsigned char* buffer, int bufferSize, int w, int h); - - virtual size_t GetWidth() const; - virtual size_t GetHeight() const; - virtual const uint8* GetYPlane() const; - virtual const uint8* GetUPlane() const; - virtual const uint8* GetVPlane() const; - virtual uint8* GetYPlane(); - virtual uint8* GetUPlane(); - virtual uint8* GetVPlane(); - virtual int32 GetYPitch() const { return video_frame_.Width(); } - virtual int32 GetUPitch() const { return video_frame_.Width() / 2; } - virtual int32 GetVPitch() const { return video_frame_.Width() / 2; } - - virtual size_t GetPixelWidth() const { return 1; } - virtual size_t GetPixelHeight() const { return 1; } - virtual int64 GetElapsedTime() const { return elapsed_time_; } - virtual int64 GetTimeStamp() const { return video_frame_.TimeStamp(); } - virtual void SetElapsedTime(int64 elapsed_time) { - elapsed_time_ = elapsed_time; - } - virtual void SetTimeStamp(int64 time_stamp) { - video_frame_.SetTimeStamp(time_stamp); - } - - virtual VideoFrame* Copy() const; - virtual size_t CopyToBuffer(uint8* buffer, size_t size) const; - virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8* buffer, - size_t size, size_t pitch_rgb) const; - virtual void StretchToPlanes(uint8* y, uint8* u, uint8* v, - int32 pitchY, int32 pitchU, int32 pitchV, - size_t width, size_t height, - bool interpolate, bool crop) const; - virtual size_t StretchToBuffer(size_t w, size_t h, uint8* buffer, size_t size, - bool interpolate, bool crop) const; - virtual void StretchToFrame(VideoFrame* target, bool interpolate, - bool crop) const; - virtual VideoFrame* Stretch(size_t w, size_t h, bool interpolate, - bool crop) const; - - private: - webrtc::VideoFrame video_frame_; - int64 elapsed_time_; -}; - -class CricketWebRTCVideoRenderer : public ExternalRenderer { - public: - CricketWebRTCVideoRenderer(cricket::VideoRenderer* renderer); - - virtual int FrameSizeChange(unsigned int width, unsigned int height, - unsigned int numberOfStreams); - virtual int DeliverFrame(unsigned char* buffer, int bufferSize); - virtual ~CricketWebRTCVideoRenderer(); - - private: - cricket::VideoRenderer* renderer_; - CricketWebRTCVideoFrame video_frame_; - unsigned int width_; - unsigned int height_; - unsigned int number_of_streams_; -}; - -class RtcVideoEngine : public ViEBaseObserver, public TraceCallback { - public: - RtcVideoEngine(); - explicit RtcVideoEngine(RtcVoiceEngine* voice_engine); - ~RtcVideoEngine(); - - bool Init(); - void Terminate(); - - RtcVideoMediaChannel* CreateChannel( - cricket::VoiceMediaChannel* voice_channel); - bool FindCodec(const cricket::VideoCodec& codec); - bool SetDefaultEncoderConfig(const cricket::VideoEncoderConfig& config); - - void RegisterChannel(RtcVideoMediaChannel* channel); - void UnregisterChannel(RtcVideoMediaChannel* channel); - - VideoEngineWrapper* video_engine() { return video_engine_.get(); } - int GetLastVideoEngineError(); - int GetCapabilities(); - bool SetOptions(int options); - //TODO - need to change this interface for webrtc - bool SetCaptureDevice(const cricket::Device* device); - bool SetLocalRenderer(cricket::VideoRenderer* renderer); - cricket::CaptureResult SetCapture(bool capture); - const std::vector& codecs() const; - void SetLogging(int min_sev, const char* filter); - - cricket::VideoEncoderConfig& default_encoder_config() { - return default_encoder_config_; - } - cricket::VideoCodec& default_codec() { - return default_codec_; - } - bool SetDefaultCodec(const cricket::VideoCodec& codec); - - void ConvertToCricketVideoCodec(const VideoCodec& in_codec, - cricket::VideoCodec& out_codec); - - void ConvertFromCricketVideoCodec(const cricket::VideoCodec& in_codec, - VideoCodec& out_codec); - - bool SetCaptureDevice(void* external_capture); - - sigslot::signal1 SignalCaptureResult; - private: - - struct VideoCodecPref { - const char* payload_name; - int payload_type; - int pref; - }; - - static const VideoCodecPref kVideoCodecPrefs[]; - int GetCodecPreference(const char* name); - - void ApplyLogging(); - bool InitVideoEngine(RtcVoiceEngine* voice_engine); - void PerformanceAlarm(const unsigned int cpuLoad); - bool ReleaseCaptureDevice(); - virtual void Print(const TraceLevel level, const char *traceString, - const int length); - - typedef std::vector VideoChannels; - - talk_base::scoped_ptr video_engine_; - VideoCaptureModule* capture_; - int capture_id_; - RtcVoiceEngine* voice_engine_; - std::vector video_codecs_; - VideoChannels channels_; - talk_base::CriticalSection channels_cs_; - bool initialized_; - int log_level_; - cricket::VideoEncoderConfig default_encoder_config_; - cricket::VideoCodec default_codec_; - bool capture_started_; - talk_base::scoped_ptr local_renderer_; -}; - -class RtcVideoMediaChannel: public cricket::VideoMediaChannel, - public webrtc::Transport { - public: - RtcVideoMediaChannel( - RtcVideoEngine* engine, cricket::VoiceMediaChannel* voice_channel); - ~RtcVideoMediaChannel(); - - bool Init(); - virtual bool SetRecvCodecs(const std::vector &codecs); - virtual bool SetSendCodecs(const std::vector &codecs); - virtual bool SetRender(bool render); - virtual bool SetSend(bool send); - virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc); - virtual bool RemoveStream(uint32 ssrc); - virtual bool SetRenderer(uint32 ssrc, cricket::VideoRenderer* renderer); - virtual bool SetExternalRenderer(uint32 ssrc, void* renderer); - virtual bool GetStats(cricket::VideoMediaInfo* info); - virtual bool SendIntraFrame(); - virtual bool RequestIntraFrame(); - - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet); - virtual void SetSendSsrc(uint32 id); - virtual bool SetRtcpCName(const std::string& cname); - virtual bool Mute(bool on); - virtual bool SetRecvRtpHeaderExtensions( - const std::vector& extensions) { return false; } - virtual bool SetSendRtpHeaderExtensions( - const std::vector& extensions) { return false; } - virtual bool SetSendBandwidth(bool autobw, int bps); - virtual bool SetOptions(int options); - - RtcVideoEngine* engine() { return engine_; } - cricket::VoiceMediaChannel* voice_channel() { return voice_channel_; } - int video_channel() { return video_channel_; } - bool sending() { return sending_; } - int GetMediaChannelId() { return video_channel_; } - - protected: - virtual int SendPacket(int channel, const void* data, int len); - virtual int SendRTCPPacket(int channel, const void* data, int len); - - private: - void EnableRtcp(); - void EnablePLI(); - void EnableTMMBR(); - - RtcVideoEngine* engine_; - cricket::VoiceMediaChannel* voice_channel_; - int video_channel_; - bool sending_; - bool render_started_; - webrtc::VideoCodec send_codec_; - talk_base::scoped_ptr remote_renderer_; -}; - -} - -#endif /* TALK_APP_WEBRTC_VIDEOMEDIAENGINE_H_ */ diff --git a/third_party_mods/libjingle/source/talk/app/voiceengine.h b/third_party_mods/libjingle/source/talk/app/voiceengine.h deleted file mode 100644 index 7de9a5a57..000000000 --- a/third_party_mods/libjingle/source/talk/app/voiceengine.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * libjingle - * Copyright 2004--2011, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#ifndef TALK_APP_WEBRTC_VOICEENGINE_H_ -#define TALK_APP_WEBRTC_VOICEENGINE_H_ - -#include "talk/base/common.h" -#include "common_types.h" -#include "voice_engine/main/interface/voe_base.h" -#include "voice_engine/main/interface/voe_codec.h" -#include "voice_engine/main/interface/voe_errors.h" -#include "voice_engine/main/interface/voe_file.h" -#include "voice_engine/main/interface/voe_hardware.h" -#include "voice_engine/main/interface/voe_network.h" -#include "voice_engine/main/interface/voe_rtp_rtcp.h" -#include "voice_engine/main/interface/voe_video_sync.h" -#include "voice_engine/main/interface/voe_volume_control.h" - -namespace webrtc { - -// Tracing helpers, for easy logging when WebRTC calls fail. -// Example: "LOG_RTCERR1(StartSend, channel);" produces the trace -// "StartSend(1) failed, err=XXXX" -// The method GetLastRtcError must be defined in the calling scope. -#define LOG_RTCERR0(func) \ - LOG_RTCERR0_EX(func, GetLastRtcError()) -#define LOG_RTCERR1(func, a1) \ - LOG_RTCERR1_EX(func, a1, GetLastRtcError()) -#define LOG_RTCERR2(func, a1, a2) \ - LOG_RTCERR2_EX(func, a1, a2, GetLastRtcError()) -#define LOG_RTCERR3(func, a1, a2, a3) \ - LOG_RTCERR3_EX(func, a1, a2, a3, GetLastRtcError()) -#define LOG_RTCERR0_EX(func, err) LOG(WARNING) \ - << "" << #func << "() failed, err=" << err -#define LOG_RTCERR1_EX(func, a1, err) LOG(WARNING) \ - << "" << #func << "(" << a1 << ") failed, err=" << err -#define LOG_RTCERR2_EX(func, a1, a2, err) LOG(WARNING) \ - << "" << #func << "(" << a1 << ", " << a2 << ") failed, err=" \ - << err -#define LOG_RTCERR3_EX(func, a1, a2, a3, err) LOG(WARNING) \ - << "" << #func << "(" << a1 << ", " << a2 << ", " << a3 \ - << ") failed, err=" << err - -// automatically handles lifetime of WebRtc VoiceEngine -class scoped_webrtc_engine { - public: - explicit scoped_webrtc_engine(VoiceEngine* e) : ptr(e) {} - // VERIFY, to ensure that there are no leaks at shutdown - ~scoped_webrtc_engine() { if (ptr) VERIFY(VoiceEngine::Delete(ptr)); } - VoiceEngine* get() const { return ptr; } - private: - VoiceEngine* ptr; -}; - -// scoped_ptr class to handle obtaining and releasing WebRTC interface pointers -template -class scoped_rtc_ptr { - public: - explicit scoped_rtc_ptr(const scoped_webrtc_engine& e) - : ptr(T::GetInterface(e.get())) {} - template - explicit scoped_rtc_ptr(E* engine) : ptr(T::GetInterface(engine)) {} - explicit scoped_rtc_ptr(T* p) : ptr(p) {} - ~scoped_rtc_ptr() { if (ptr) ptr->Release(); } - T* operator->() const { return ptr; } - T* get() const { return ptr; } - - // Queries the engine for the wrapped type and releases the current pointer. - template - void reset(E* engine) { - reset(); - if (engine) - ptr = T::GetInterface(engine); - } - - // Releases the current pointer. - void reset() { - if (ptr) { - ptr->Release(); - ptr = NULL; - } - } - - private: - T* ptr; -}; - -// Utility class for aggregating the various WebRTC interface. -// Fake implementations can also be injected for testing. -class RtcWrapper { - public: - RtcWrapper() - : engine_(VoiceEngine::Create()), - base_(engine_), codec_(engine_), file_(engine_), - hw_(engine_), network_(engine_), rtp_(engine_), - sync_(engine_), volume_(engine_) { - - } - RtcWrapper(VoEBase* base, VoECodec* codec, VoEFile* file, - VoEHardware* hw, VoENetwork* network, - VoERTP_RTCP* rtp, VoEVideoSync* sync, - VoEVolumeControl* volume) - : engine_(NULL), - base_(base), codec_(codec), file_(file), - hw_(hw), network_(network), rtp_(rtp), - sync_(sync), volume_(volume) { - - } - virtual ~RtcWrapper() {} - VoiceEngine* engine() { return engine_.get(); } - VoEBase* base() { return base_.get(); } - VoECodec* codec() { return codec_.get(); } - VoEFile* file() { return file_.get(); } - VoEHardware* hw() { return hw_.get(); } - VoENetwork* network() { return network_.get(); } - VoERTP_RTCP* rtp() { return rtp_.get(); } - VoEVideoSync* sync() { return sync_.get(); } - VoEVolumeControl* volume() { return volume_.get(); } - int error() { return base_->LastError(); } - - private: - scoped_webrtc_engine engine_; - scoped_rtc_ptr base_; - scoped_rtc_ptr codec_; - scoped_rtc_ptr file_; - scoped_rtc_ptr hw_; - scoped_rtc_ptr network_; - scoped_rtc_ptr rtp_; - scoped_rtc_ptr sync_; - scoped_rtc_ptr volume_; -}; -} //namespace webrtc - -#endif // TALK_APP_WEBRTC_VOICEENGINE_H_ diff --git a/third_party_mods/libjingle/source/talk/app/voicemediaengine.cc b/third_party_mods/libjingle/source/talk/app/voicemediaengine.cc deleted file mode 100644 index 326009dbc..000000000 --- a/third_party_mods/libjingle/source/talk/app/voicemediaengine.cc +++ /dev/null @@ -1,966 +0,0 @@ -/* - * libjingle - * Copyright 2004--2011, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "talk/app/voicemediaengine.h" - -#include -#include -#include -#include - -#ifdef PLATFORM_CHROMIUM -#include "content/renderer/renderer_webrtc_audio_device_impl.h" -#else -#include "modules/audio_device/main/interface/audio_device.h" -#endif -#include "talk/base/base64.h" -#include "talk/base/byteorder.h" -#include "talk/base/common.h" -#include "talk/base/helpers.h" -#include "talk/base/logging.h" -#include "talk/base/stringencode.h" - -namespace webrtc { - -static void LogMultiline(talk_base::LoggingSeverity sev, char* text) { - const char* delim = "\r\n"; - for (char* tok = strtok(text, delim); tok; tok = strtok(NULL, delim)) { - LOG_V(sev) << tok; - } -} - -// RtcVoiceEngine -const RtcVoiceEngine::CodecPref RtcVoiceEngine::kCodecPrefs[] = { - { "ISAC", 16000 }, - { "ISAC", 32000 }, - { "ISACLC", 16000 }, - { "speex", 16000 }, - { "IPCMWB", 16000 }, - { "G722", 16000 }, - { "iLBC", 8000 }, - { "speex", 8000 }, - { "GSM", 8000 }, - { "EG711U", 8000 }, - { "EG711A", 8000 }, - { "PCMU", 8000 }, - { "PCMA", 8000 }, - { "CN", 32000 }, - { "CN", 16000 }, - { "CN", 8000 }, - { "red", 8000 }, - { "telephone-event", 8000 }, -}; - -RtcVoiceEngine::RtcVoiceEngine() - : rtc_wrapper_(new RtcWrapper()), - log_level_(kDefaultLogSeverity), - adm_(NULL) { - Construct(); -} - -RtcVoiceEngine::RtcVoiceEngine(RtcWrapper* rtc_wrapper) - : rtc_wrapper_(rtc_wrapper), - log_level_(kDefaultLogSeverity), - adm_(NULL) { - Construct(); -} - -void RtcVoiceEngine::Construct() { - LOG(INFO) << "RtcVoiceEngine::RtcVoiceEngine"; - ApplyLogging(); - - if (rtc_wrapper_->base()->RegisterVoiceEngineObserver(*this) == -1) { - LOG_RTCERR0(RegisterVoiceEngineObserver); - } - - // Load our audio codec list - LOG(INFO) << "WebRTC VoiceEngine codecs:"; - int ncodecs = rtc_wrapper_->codec()->NumOfCodecs(); - for (int i = 0; i < ncodecs; ++i) { - CodecInst gcodec; - if (rtc_wrapper_->codec()->GetCodec(i, gcodec) >= 0) { - int pref = GetCodecPreference(gcodec.plname, gcodec.plfreq); - if (pref != -1) { - if (gcodec.rate == -1) gcodec.rate = 0; - cricket::AudioCodec codec(gcodec.pltype, gcodec.plname, gcodec.plfreq, - gcodec.rate, gcodec.channels, pref); - LOG(INFO) << gcodec.plname << "/" << gcodec.plfreq << "/" \ - << gcodec.channels << " " << gcodec.pltype; - codecs_.push_back(codec); - } - } - } - // Make sure they are in local preference order - std::sort(codecs_.begin(), codecs_.end(), &cricket::AudioCodec::Preferable); -} - -RtcVoiceEngine::~RtcVoiceEngine() { - LOG(INFO) << "RtcVoiceEngine::~RtcVoiceEngine"; - if (rtc_wrapper_->base()->DeRegisterVoiceEngineObserver() == -1) { - LOG_RTCERR0(DeRegisterVoiceEngineObserver); - } - rtc_wrapper_.reset(); - if (adm_) { - AudioDeviceModule::Destroy(adm_); - adm_ = NULL; - } -} - -bool RtcVoiceEngine::Init() { - LOG(INFO) << "RtcVoiceEngine::Init"; - bool res = InitInternal(); - if (res) { - LOG(INFO) << "RtcVoiceEngine::Init Done!"; - } else { - LOG(LERROR) << "RtcVoiceEngine::Init failed"; - Terminate(); - } - return res; -} - -bool RtcVoiceEngine::InitInternal() { - // Temporarily turn logging level up for the Init call - int old_level = log_level_; - log_level_ = talk_base::_min(log_level_, - static_cast(talk_base::INFO)); - ApplyLogging(); - - if (!adm_) { -#ifdef PLATFORM_CHROMIUM - adm_ = new RendererWebRtcAudioDeviceImpl(1440, 1440, 1, 1, 48000, 48000); -#else - adm_ = AudioDeviceModule::Create(0); -#endif - - if (rtc_wrapper_->base()->RegisterAudioDeviceModule(*adm_) == -1) { - LOG_RTCERR0_EX(Init, rtc_wrapper_->error()); - return false; - } - } - - // Init WebRTC VoiceEngine, enabling AEC logging if specified in SetLogging. - if (rtc_wrapper_->base()->Init() == -1) { - LOG_RTCERR0_EX(Init, rtc_wrapper_->error()); - return false; - } - - // Restore the previous log level - log_level_ = old_level; - ApplyLogging(); - - // Log the WebRTC version info - char buffer[1024] = ""; - rtc_wrapper_->base()->GetVersion(buffer); - LOG(INFO) << "WebRTC VoiceEngine Version:"; - LogMultiline(talk_base::INFO, buffer); - - // Turn on AEC and AGC by default. - if (!SetOptions( - cricket::MediaEngine::ECHO_CANCELLATION | cricket::MediaEngine::AUTO_GAIN_CONTROL)) { - return false; - } - - // Print our codec list again for the call diagnostic log - LOG(INFO) << "WebRTC VoiceEngine codecs:"; - for (std::vector::const_iterator it = codecs_.begin(); - it != codecs_.end(); ++it) { - LOG(INFO) << it->name << "/" << it->clockrate << "/" - << it->channels << " " << it->id; - } - return true; -} - -bool RtcVoiceEngine::SetDevices(const cricket::Device* in_device, - const cricket::Device* out_device) { - LOG(INFO) << "RtcVoiceEngine::SetDevices"; - // Currently we always use the default device, so do nothing here. - return true; -} - -void RtcVoiceEngine::Terminate() { - LOG(INFO) << "RtcVoiceEngine::Terminate"; - - rtc_wrapper_->base()->Terminate(); -} - -int RtcVoiceEngine::GetCapabilities() { - return cricket::MediaEngine::AUDIO_SEND | cricket::MediaEngine::AUDIO_RECV; -} - -cricket::VoiceMediaChannel *RtcVoiceEngine::CreateChannel() { - RtcVoiceMediaChannel* ch = new RtcVoiceMediaChannel(this); - if (!ch->valid()) { - delete ch; - ch = NULL; - } - return ch; -} - -bool RtcVoiceEngine::SetOptions(int options) { - - return true; -} - -bool RtcVoiceEngine::FindAudioDeviceId( - bool is_input, const std::string& dev_name, int dev_id, int* rtc_id) { - return false; -} - -bool RtcVoiceEngine::GetOutputVolume(int* level) { - unsigned int ulevel; - if (rtc_wrapper_->volume()->GetSpeakerVolume(ulevel) == -1) { - LOG_RTCERR1(GetSpeakerVolume, level); - return false; - } - *level = ulevel; - return true; -} - -bool RtcVoiceEngine::SetOutputVolume(int level) { - ASSERT(level >= 0 && level <= 255); - if (rtc_wrapper_->volume()->SetSpeakerVolume(level) == -1) { - LOG_RTCERR1(SetSpeakerVolume, level); - return false; - } - return true; -} - -int RtcVoiceEngine::GetInputLevel() { - unsigned int ulevel; - return (rtc_wrapper_->volume()->GetSpeechInputLevel(ulevel) != -1) ? - static_cast(ulevel) : -1; -} - -bool RtcVoiceEngine::SetLocalMonitor(bool enable) { - return true; -} - -const std::vector& RtcVoiceEngine::codecs() { - return codecs_; -} - -bool RtcVoiceEngine::FindCodec(const cricket::AudioCodec& in) { - return FindRtcCodec(in, NULL); -} - -bool RtcVoiceEngine::FindRtcCodec(const cricket::AudioCodec& in, CodecInst* out) { - int ncodecs = rtc_wrapper_->codec()->NumOfCodecs(); - for (int i = 0; i < ncodecs; ++i) { - CodecInst gcodec; - if (rtc_wrapper_->codec()->GetCodec(i, gcodec) >= 0) { - cricket::AudioCodec codec(gcodec.pltype, gcodec.plname, - gcodec.plfreq, gcodec.rate, gcodec.channels, 0); - if (codec.Matches(in)) { - if (out) { - // If the codec is VBR and an explicit rate is specified, use it. - if (in.bitrate != 0 && gcodec.rate == -1) { - gcodec.rate = in.bitrate; - } - *out = gcodec; - } - return true; - } - } - } - return false; -} - -void RtcVoiceEngine::SetLogging(int min_sev, const char* filter) { - log_level_ = min_sev; - - std::vector opts; - talk_base::tokenize(filter, ' ', &opts); - - // voice log level - ApplyLogging(); -} - -int RtcVoiceEngine::GetLastRtcError() { - return rtc_wrapper_->error(); -} - -void RtcVoiceEngine::ApplyLogging() { - int filter = 0; - switch (log_level_) { - case talk_base::INFO: filter |= kTraceAll; // fall through - case talk_base::WARNING: filter |= kTraceWarning; // fall through - case talk_base::LERROR: filter |= kTraceError | kTraceCritical; - } -} - -void RtcVoiceEngine::Print(const TraceLevel level, - const char* traceString, const int length) { - talk_base::LoggingSeverity sev = talk_base::INFO; - if (level == kTraceError || level == kTraceCritical) - sev = talk_base::LERROR; - else if (level == kTraceWarning) - sev = talk_base::WARNING; - else if (level == kTraceStateInfo) - sev = talk_base::INFO; - - if (sev >= log_level_) { - // Skip past webrtc boilerplate prefix text - if (length <= 70) { - std::string msg(traceString, length); - LOG(LERROR) << "Malformed WebRTC log message: "; - LOG_V(sev) << msg; - } else { - std::string msg(traceString + 70, length - 71); - LOG_V(sev) << "VoE:" << msg; - } - } -} - -void RtcVoiceEngine::CallbackOnError(const int err_code, - const int channel_num) { - talk_base::CritScope lock(&channels_cs_); - RtcVoiceMediaChannel* channel = NULL; - uint32 ssrc = 0; - LOG(WARNING) << "WebRTC error " << err_code << " reported on channel " - << channel_num << "."; - if (FindChannelAndSsrc(channel_num, &channel, &ssrc)) { - ASSERT(channel != NULL); - channel->OnError(ssrc, err_code); - } else { - LOG(LERROR) << "WebRTC channel " << channel_num - << " could not be found in the channel list when error reported."; - } -} - -int RtcVoiceEngine::GetCodecPreference(const char *name, int clockrate) { - for (size_t i = 0; i < ARRAY_SIZE(kCodecPrefs); ++i) { - if ((strcmp(kCodecPrefs[i].name, name) == 0) && - (kCodecPrefs[i].clockrate == clockrate)) - return ARRAY_SIZE(kCodecPrefs) - i; - } - LOG(WARNING) << "Unexpected codec \"" << name << "/" << clockrate << "\""; - return -1; -} - -bool RtcVoiceEngine::FindChannelAndSsrc( - int channel_num, RtcVoiceMediaChannel** channel, uint32* ssrc) const { - ASSERT(channel != NULL && ssrc != NULL); - - *channel = NULL; - *ssrc = 0; - // Find corresponding channel and ssrc - for (ChannelList::const_iterator it = channels_.begin(); - it != channels_.end(); ++it) { - ASSERT(*it != NULL); - if ((*it)->FindSsrc(channel_num, ssrc)) { - *channel = *it; - return true; - } - } - - return false; -} - -void RtcVoiceEngine::RegisterChannel(RtcVoiceMediaChannel *channel) { - talk_base::CritScope lock(&channels_cs_); - channels_.push_back(channel); -} - -void RtcVoiceEngine::UnregisterChannel(RtcVoiceMediaChannel *channel) { - talk_base::CritScope lock(&channels_cs_); - ChannelList::iterator i = std::find(channels_.begin(), - channels_.end(), - channel); - if (i != channels_.end()) { - channels_.erase(i); - } -} - -// RtcVoiceMediaChannel -RtcVoiceMediaChannel::RtcVoiceMediaChannel(RtcVoiceEngine *engine) - : RtcMediaChannel(engine, - engine->webrtc()->base()->CreateChannel()), - channel_options_(0), playout_(false), send_(cricket::SEND_NOTHING) { - engine->RegisterChannel(this); - LOG(INFO) << "RtcVoiceMediaChannel::RtcVoiceMediaChannel " - << audio_channel(); - - // Register external transport - if (engine->webrtc()->network()->RegisterExternalTransport( - audio_channel(), *static_cast(this)) == -1) { - LOG_RTCERR2(RegisterExternalTransport, audio_channel(), this); - } - - // Enable RTCP (for quality stats and feedback messages) - EnableRtcp(audio_channel()); - - // Create a random but nonzero send SSRC - SetSendSsrc(talk_base::CreateRandomNonZeroId()); -} - -RtcVoiceMediaChannel::~RtcVoiceMediaChannel() { - LOG(INFO) << "RtcVoiceMediaChannel::~RtcVoiceMediaChannel " - << audio_channel(); - - // DeRegister external transport - if (engine()->webrtc()->network()->DeRegisterExternalTransport( - audio_channel()) == -1) { - LOG_RTCERR1(DeRegisterExternalTransport, audio_channel()); - } - - // Unregister ourselves from the engine. - engine()->UnregisterChannel(this); - // Remove any remaining streams. - while (!mux_channels_.empty()) { - RemoveStream(mux_channels_.begin()->first); - } - // Delete the primary channel. - if (engine()->webrtc()->base()->DeleteChannel(audio_channel()) == -1) { - LOG_RTCERR1(DeleteChannel, audio_channel()); - } -} - -bool RtcVoiceMediaChannel::SetOptions(int flags) { - // Always accept flags that are unchanged. - if (channel_options_ == flags) { - return true; - } - - // Reject new options if we're already sending. - if (send_ != cricket::SEND_NOTHING) { - return false; - } - // Save the options, to be interpreted where appropriate. - channel_options_ = flags; - return true; -} - -bool RtcVoiceMediaChannel::SetRecvCodecs( - const std::vector& codecs) { - // Update our receive payload types to match what we offered. This only is - // an issue when a different entity (i.e. a server) is generating the offer - // for us. - bool ret = true; - for (std::vector::const_iterator i = codecs.begin(); - i != codecs.end() && ret; ++i) { - CodecInst gcodec; - if (engine()->FindRtcCodec(*i, &gcodec)) { - if (gcodec.pltype != i->id) { - LOG(INFO) << "Updating payload type for " << gcodec.plname - << " from " << gcodec.pltype << " to " << i->id; - gcodec.pltype = i->id; - if (engine()->webrtc()->codec()->SetRecPayloadType( - audio_channel(), gcodec) == -1) { - LOG_RTCERR1(SetRecPayloadType, audio_channel()); - ret = false; - } - } - } else { - LOG(WARNING) << "Unknown codec " << i->name; - ret = false; - } - } - - return ret; -} - -bool RtcVoiceMediaChannel::SetSendCodecs( - const std::vector& codecs) { - bool first = true; - CodecInst send_codec; - memset(&send_codec, 0, sizeof(send_codec)); - - for (std::vector::const_iterator i = codecs.begin(); - i != codecs.end(); ++i) { - CodecInst gcodec; - if (!engine()->FindRtcCodec(*i, &gcodec)) - continue; - - // We'll use the first codec in the list to actually send audio data. - // Be sure to use the payload type requested by the remote side. - if (first) { - send_codec = gcodec; - send_codec.pltype = i->id; - first = false; - } - } - - // If we're being asked to set an empty list of codecs, due to a buggy client, - // choose the most common format: PCMU - if (first) { - LOG(WARNING) << "Received empty list of codecs; using PCMU/8000"; - cricket::AudioCodec codec(0, "PCMU", 8000, 0, 1, 0); - engine()->FindRtcCodec(codec, &send_codec); - } - - // Set the codec. - LOG(INFO) << "Selected voice codec " << send_codec.plname - << "/" << send_codec.plfreq; - if (engine()->webrtc()->codec()->SetSendCodec(audio_channel(), - send_codec) == -1) { - LOG_RTCERR1(SetSendCodec, audio_channel()); - return false; - } - - return true; -} - -bool RtcVoiceMediaChannel::SetPlayout(bool playout) { - if (playout_ == playout) { - return true; - } - - bool result = true; - if (mux_channels_.empty()) { - // Only toggle the default channel if we don't have any other channels. - result = SetPlayout(audio_channel(), playout); - } - for (ChannelMap::iterator it = mux_channels_.begin(); - it != mux_channels_.end() && result; ++it) { - if (!SetPlayout(it->second, playout)) { - LOG(LERROR) << "SetPlayout " << playout << " on channel " << it->second - << " failed"; - result = false; - } - } - - if (result) { - playout_ = playout; - } - return result; -} - -bool RtcVoiceMediaChannel::GetPlayout() { - return playout_; -} - -bool RtcVoiceMediaChannel::SetSend(cricket::SendFlags send) { - if (send_ == send) { - return true; - } - - if (send == cricket::SEND_MICROPHONE) { - if (sequence_number() != -1) { - if (engine()->webrtc()->sync()->SetInitSequenceNumber( - audio_channel(), sequence_number() + 1) == -1) { - LOG_RTCERR2(SetInitSequenceNumber, audio_channel(), - sequence_number() + 1); - } - } - if (engine()->webrtc()->base()->StartSend(audio_channel()) == -1) { - LOG_RTCERR1(StartSend, audio_channel()); - return false; - } - if (engine()->webrtc()->file()->StopPlayingFileAsMicrophone( - audio_channel()) == -1) { - LOG_RTCERR1(StopPlayingFileAsMicrophone, audio_channel()); - return false; - } - } else { // SEND_NOTHING - if (engine()->webrtc()->base()->StopSend(audio_channel()) == -1) { - LOG_RTCERR1(StopSend, audio_channel()); - } - } - send_ = send; - return true; -} - -cricket::SendFlags RtcVoiceMediaChannel::GetSend() { - return send_; -} - -bool RtcVoiceMediaChannel::AddStream(uint32 ssrc) { - talk_base::CritScope lock(&mux_channels_cs_); - - if (mux_channels_.find(ssrc) != mux_channels_.end()) { - return false; - } - - // Create a new channel for receiving audio data. - int channel = engine()->webrtc()->base()->CreateChannel(); - if (channel == -1) { - LOG_RTCERR0(CreateChannel); - return false; - } - - // Configure to use external transport, like our default channel. - if (engine()->webrtc()->network()->RegisterExternalTransport( - channel, *this) == -1) { - LOG_RTCERR2(SetExternalTransport, channel, this); - return false; - } - - // Use the same SSRC as our default channel (so the RTCP reports are correct). - unsigned int send_ssrc; - VoERTP_RTCP* rtp = engine()->webrtc()->rtp(); - if (rtp->GetLocalSSRC(audio_channel(), send_ssrc) == -1) { - LOG_RTCERR2(GetSendSSRC, channel, send_ssrc); - return false; - } - if (rtp->SetLocalSSRC(channel, send_ssrc) == -1) { - LOG_RTCERR2(SetSendSSRC, channel, send_ssrc); - return false; - } - - if (mux_channels_.empty() && GetPlayout()) { - LOG(INFO) << "Disabling playback on the default voice channel"; - SetPlayout(audio_channel(), false); - } - - mux_channels_[ssrc] = channel; - - LOG(INFO) << "New audio stream " << ssrc << " registered to WebRTC channel #" - << channel << "."; - return SetPlayout(channel, playout_); - - -} - -bool RtcVoiceMediaChannel::RemoveStream(uint32 ssrc) { - talk_base::CritScope lock(&mux_channels_cs_); - ChannelMap::iterator it = mux_channels_.find(ssrc); - - if (it != mux_channels_.end()) { - if (engine()->webrtc()->network()->DeRegisterExternalTransport( - it->second) == -1) { - LOG_RTCERR1(DeRegisterExternalTransport, it->second); - } - - LOG(INFO) << "Removing audio stream " << ssrc << " with WebRTC channel #" - << it->second << "."; - if (engine()->webrtc()->base()->DeleteChannel(it->second) == -1) { - LOG_RTCERR1(DeleteChannel, audio_channel()); - return false; - } - - mux_channels_.erase(it); - if (mux_channels_.empty() && GetPlayout()) { - // The last stream was removed. We can now enable the default - // channel for new channels to be played out immediately without - // waiting for AddStream messages. - // TODO(oja): Does the default channel still have it's CN state? - LOG(INFO) << "Enabling playback on the default voice channel"; - SetPlayout(audio_channel(), true); - } - } - return true; -} - -bool RtcVoiceMediaChannel::GetActiveStreams(cricket::AudioInfo::StreamList* actives) { - actives->clear(); - for (ChannelMap::iterator it = mux_channels_.begin(); - it != mux_channels_.end(); ++it) { - int level = GetOutputLevel(it->second); - if (level > 0) { - actives->push_back(std::make_pair(it->first, level)); - } - } - return true; -} - -int RtcVoiceMediaChannel::GetOutputLevel() { - // return the highest output level of all streams - int highest = GetOutputLevel(audio_channel()); - for (ChannelMap::iterator it = mux_channels_.begin(); - it != mux_channels_.end(); ++it) { - int level = GetOutputLevel(it->second); - highest = talk_base::_max(level, highest); - } - return highest; -} - -bool RtcVoiceMediaChannel::SetRingbackTone(const char *buf, int len) { - return true; -} - -bool RtcVoiceMediaChannel::PlayRingbackTone(uint32 ssrc, bool play, bool loop) { - return true; -} - -bool RtcVoiceMediaChannel::PlayRingbackTone(bool play, bool loop) { - return true; -} - -bool RtcVoiceMediaChannel::PressDTMF(int event, bool playout) { - return true; -} - -void RtcVoiceMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { - // Pick which channel to send this packet to. If this packet doesn't match - // any multiplexed streams, just send it to the default channel. Otherwise, - // send it to the specific decoder instance for that stream. - int which_channel = GetChannel( - ParseSsrc(packet->data(), packet->length(), false)); - if (which_channel == -1) { - which_channel = audio_channel(); - } - - engine()->webrtc()->network()->ReceivedRTPPacket(which_channel, - packet->data(), - packet->length()); -} - -void RtcVoiceMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) { - // See above. - int which_channel = GetChannel( - ParseSsrc(packet->data(), packet->length(), true)); - if (which_channel == -1) { - which_channel = audio_channel(); - } - - engine()->webrtc()->network()->ReceivedRTCPPacket(which_channel, - packet->data(), - packet->length()); -} - -void RtcVoiceMediaChannel::SetSendSsrc(uint32 ssrc) { - if (engine()->webrtc()->rtp()->SetLocalSSRC(audio_channel(), ssrc) - == -1) { - LOG_RTCERR2(SetSendSSRC, audio_channel(), ssrc); - } -} - -bool RtcVoiceMediaChannel::SetRtcpCName(const std::string& cname) { - if (engine()->webrtc()->rtp()->SetRTCP_CNAME(audio_channel(), - cname.c_str()) == -1) { - LOG_RTCERR2(SetRTCP_CNAME, audio_channel(), cname); - return false; - } - return true; -} - -bool RtcVoiceMediaChannel::Mute(bool muted) { - if (engine()->webrtc()->volume()->SetInputMute(audio_channel(), - muted) == -1) { - LOG_RTCERR2(SetInputMute, audio_channel(), muted); - return false; - } - return true; -} - -bool RtcVoiceMediaChannel::GetStats(cricket::VoiceMediaInfo* info) { - CallStatistics cs; - unsigned int ssrc; - CodecInst codec; - unsigned int level; - - // Fill in the sender info, based on what we know, and what the - // remote side told us it got from its RTCP report. - cricket::VoiceSenderInfo sinfo; - memset(&sinfo, 0, sizeof(sinfo)); - - // Data we obtain locally. - memset(&cs, 0, sizeof(cs)); - if (engine()->webrtc()->rtp()->GetRTCPStatistics( - audio_channel(), cs) == -1 || - engine()->webrtc()->rtp()->GetLocalSSRC(audio_channel(), ssrc) == -1) - { - return false; - } - - sinfo.ssrc = ssrc; - sinfo.bytes_sent = cs.bytesSent; - sinfo.packets_sent = cs.packetsSent; - // RTT isn't known until a RTCP report is received. Until then, WebRTC - // returns 0 to indicate an error value. - sinfo.rtt_ms = (cs.rttMs > 0) ? cs.rttMs : -1; - - // Data from the last remote RTCP report. - unsigned int ntp_high, ntp_low, timestamp, ptimestamp, jitter; - unsigned short loss; // NOLINT - if (engine()->webrtc()->rtp()->GetRemoteRTCPData(audio_channel(), - ntp_high, ntp_low, timestamp, ptimestamp, &jitter, &loss) != -1 && - engine()->webrtc()->codec()->GetSendCodec(audio_channel(), - codec) != -1) { - // Convert Q8 to floating point. - sinfo.fraction_lost = static_cast(loss) / (1 << 8); - // Convert samples to milliseconds. - if (codec.plfreq / 1000 > 0) { - sinfo.jitter_ms = jitter / (codec.plfreq / 1000); - } - } else { - sinfo.fraction_lost = -1; - sinfo.jitter_ms = -1; - } - - sinfo.packets_lost = -1; - sinfo.ext_seqnum = -1; - - // Local speech level. - sinfo.audio_level = (engine()->webrtc()->volume()-> - GetSpeechInputLevelFullRange(level) != -1) ? level : -1; - info->senders.push_back(sinfo); - - // Build the list of receivers, one for each mux channel, or 1 in a 1:1 call. - std::vector channels; - for (ChannelMap::const_iterator it = mux_channels_.begin(); - it != mux_channels_.end(); ++it) { - channels.push_back(it->second); - } - if (channels.empty()) { - channels.push_back(audio_channel()); - } - - // Get the SSRC and stats for each receiver, based on our own calculations. - for (std::vector::const_iterator it = channels.begin(); - it != channels.end(); ++it) { - memset(&cs, 0, sizeof(cs)); - if (engine()->webrtc()->rtp()->GetRemoteSSRC(*it, ssrc) != -1 && - engine()->webrtc()->rtp()->GetRTCPStatistics(*it, cs) != -1 && - engine()->webrtc()->codec()->GetRecCodec(*it, codec) != -1) { - cricket::VoiceReceiverInfo rinfo; - memset(&rinfo, 0, sizeof(rinfo)); - rinfo.ssrc = ssrc; - rinfo.bytes_rcvd = cs.bytesReceived; - rinfo.packets_rcvd = cs.packetsReceived; - // The next four fields are from the most recently sent RTCP report. - // Convert Q8 to floating point. - rinfo.fraction_lost = static_cast(cs.fractionLost) / (1 << 8); - rinfo.packets_lost = cs.cumulativeLost; - rinfo.ext_seqnum = cs.extendedMax; - // Convert samples to milliseconds. - if (codec.plfreq / 1000 > 0) { - rinfo.jitter_ms = cs.jitterSamples / (codec.plfreq / 1000); - } - // Get speech level. - rinfo.audio_level = (engine()->webrtc()->volume()-> - GetSpeechOutputLevelFullRange(*it, level) != -1) ? level : -1; - info->receivers.push_back(rinfo); - } - } - - return true; -} - -void RtcVoiceMediaChannel::GetLastMediaError( - uint32* ssrc, VoiceMediaChannel::Error* error) { - ASSERT(ssrc != NULL); - ASSERT(error != NULL); - FindSsrc(audio_channel(), ssrc); - *error = WebRTCErrorToChannelError(GetLastRtcError()); -} - -bool RtcVoiceMediaChannel::FindSsrc(int channel_num, uint32* ssrc) { - talk_base::CritScope lock(&mux_channels_cs_); - ASSERT(ssrc != NULL); - if (channel_num == audio_channel()) { - unsigned local_ssrc = 0; - // This is a sending channel. - if (engine()->webrtc()->rtp()->GetLocalSSRC( - channel_num, local_ssrc) != -1) { - *ssrc = local_ssrc; - } - return true; - } else { - // Check whether this is a receiving channel. - for (ChannelMap::const_iterator it = mux_channels_.begin(); - it != mux_channels_.end(); ++it) { - if (it->second == channel_num) { - *ssrc = it->first; - return true; - } - } - } - return false; -} - -void RtcVoiceMediaChannel::OnError(uint32 ssrc, int error) { - SignalMediaError(ssrc, WebRTCErrorToChannelError(error)); -} - -int RtcVoiceMediaChannel::GetChannel(uint32 ssrc) { - ChannelMap::iterator it = mux_channels_.find(ssrc); - return (it != mux_channels_.end()) ? it->second : -1; -} - -int RtcVoiceMediaChannel::GetOutputLevel(int channel) { - unsigned int ulevel; - int ret = - engine()->webrtc()->volume()->GetSpeechOutputLevel(channel, ulevel); - return (ret == 0) ? static_cast(ulevel) : -1; -} - -bool RtcVoiceMediaChannel::EnableRtcp(int channel) { - if (engine()->webrtc()->rtp()->SetRTCPStatus(channel, true) == -1) { - LOG_RTCERR2(SetRTCPStatus, audio_channel(), 1); - return false; - } - return true; -} - -bool RtcVoiceMediaChannel::SetPlayout(int channel, bool playout) { - if (playout) { - LOG(INFO) << "Starting playout for channel #" << channel; - if (engine()->webrtc()->base()->StartPlayout(channel) == -1) { - LOG_RTCERR1(StartPlayout, channel); - return false; - } - } else { - LOG(INFO) << "Stopping playout for channel #" << channel; - engine()->webrtc()->base()->StopPlayout(channel); - } - return true; -} - -uint32 RtcVoiceMediaChannel::ParseSsrc(const void* data, size_t len, - bool rtcp) { - size_t ssrc_pos = (!rtcp) ? 8 : 4; - uint32 ssrc = 0; - if (len >= (ssrc_pos + sizeof(ssrc))) { - ssrc = talk_base::GetBE32(static_cast(data) + ssrc_pos); - } - return ssrc; -} - -// Convert WebRTC error code into VoiceMediaChannel::Error enum. -cricket::VoiceMediaChannel::Error RtcVoiceMediaChannel::WebRTCErrorToChannelError( - int err_code) { - switch (err_code) { - case 0: - return ERROR_NONE; - case VE_CANNOT_START_RECORDING: - case VE_MIC_VOL_ERROR: - case VE_GET_MIC_VOL_ERROR: - case VE_CANNOT_ACCESS_MIC_VOL: - return ERROR_REC_DEVICE_OPEN_FAILED; - case VE_SATURATION_WARNING: - return ERROR_REC_DEVICE_SATURATION; - case VE_REC_DEVICE_REMOVED: - return ERROR_REC_DEVICE_REMOVED; - case VE_RUNTIME_REC_WARNING: - case VE_RUNTIME_REC_ERROR: - return ERROR_REC_RUNTIME_ERROR; - case VE_CANNOT_START_PLAYOUT: - case VE_SPEAKER_VOL_ERROR: - case VE_GET_SPEAKER_VOL_ERROR: - case VE_CANNOT_ACCESS_SPEAKER_VOL: - return ERROR_PLAY_DEVICE_OPEN_FAILED; - case VE_RUNTIME_PLAY_WARNING: - case VE_RUNTIME_PLAY_ERROR: - return ERROR_PLAY_RUNTIME_ERROR; - default: - return VoiceMediaChannel::ERROR_OTHER; - } -} - -} // namespace webrtc - diff --git a/third_party_mods/libjingle/source/talk/app/voicemediaengine.h b/third_party_mods/libjingle/source/talk/app/voicemediaengine.h deleted file mode 100644 index 639d91fa7..000000000 --- a/third_party_mods/libjingle/source/talk/app/voicemediaengine.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - * libjingle - * Copyright 2004--2011, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_APP_WEBRTC_AUDIOMEDIAENGINE_H_ -#define TALK_APP_WEBRTC_AUDIOMEDIAENGINE_H_ - -#include -#include -#include - -#include "talk/base/buffer.h" -#include "talk/base/byteorder.h" -#include "talk/base/logging.h" -#include "talk/base/scoped_ptr.h" -#include "talk/base/stream.h" -#include "talk/session/phone/channel.h" -#include "talk/session/phone/mediaengine.h" -#include "talk/session/phone/rtputils.h" -#include "talk/app/voiceengine.h" - -namespace cricket { -class SoundclipMedia; -class VoiceMediaChannel; -} -namespace webrtc { - -// MonitorStream is used to monitor a stream coming from WebRTC. -// For now we just dump the data. -class MonitorStream : public OutStream { - virtual bool Write(const void *buf, int len) { - return true; - } -}; - -class AudioDeviceModule; -class RtcVoiceMediaChannel; - -// RtcVoiceEngine is a class to be used with CompositeMediaEngine. -// It uses the WebRTC VoiceEngine library for audio handling. -class RtcVoiceEngine - : public VoiceEngineObserver, - public TraceCallback { - public: - RtcVoiceEngine(); // NOLINT - // Dependency injection for testing. - explicit RtcVoiceEngine(RtcWrapper* rtc_wrapper); - ~RtcVoiceEngine(); - bool Init(); - void Terminate(); - - int GetCapabilities(); - cricket::VoiceMediaChannel* CreateChannel(); - cricket::SoundclipMedia* CreateSoundclip() { return NULL; } - bool SetDevices(const cricket::Device* in_device, - const cricket::Device* out_device); - bool SetOptions(int options); - bool GetOutputVolume(int* level); - bool SetOutputVolume(int level); - int GetInputLevel(); - bool SetLocalMonitor(bool enable); - - const std::vector& codecs(); - bool FindCodec(const cricket::AudioCodec& codec); - bool FindRtcCodec(const cricket::AudioCodec& codec, CodecInst* gcodec); - - void SetLogging(int min_sev, const char* filter); - - // For tracking WebRTC channels. Needed because we have to pause them - // all when switching devices. - // May only be called by RtcVoiceMediaChannel. - void RegisterChannel(RtcVoiceMediaChannel *channel); - void UnregisterChannel(RtcVoiceMediaChannel *channel); - - RtcWrapper* webrtc() { return rtc_wrapper_.get(); } - int GetLastRtcError(); - - private: - typedef std::vector ChannelList; - - struct CodecPref { - const char* name; - int clockrate; - }; - - void Construct(); - bool InitInternal(); - void ApplyLogging(); - virtual void Print(const TraceLevel level, - const char* traceString, const int length); - virtual void CallbackOnError(const int errCode, const int channel); - static int GetCodecPreference(const char *name, int clockrate); - // Given the device type, name, and id, find WebRTC's device id. Return true and - // set the output parameter rtc_id if successful. - bool FindAudioDeviceId( - bool is_input, const std::string& dev_name, int dev_id, int* rtc_id); - bool FindChannelAndSsrc(int channel_num, - RtcVoiceMediaChannel** channel, - uint32* ssrc) const; - - static const int kDefaultLogSeverity = talk_base::LS_WARNING; - static const CodecPref kCodecPrefs[]; - - // The primary instance of WebRTC VoiceEngine. - talk_base::scoped_ptr rtc_wrapper_; - int log_level_; - std::vector codecs_; - talk_base::scoped_ptr monitor_; - // TODO: Can't use scoped_ptr here since ~AudioDeviceModule is protected. - AudioDeviceModule* adm_; - ChannelList channels_; - talk_base::CriticalSection channels_cs_; -}; - -// RtcMediaChannel is a class that implements the common WebRTC channel -// functionality. -template -class RtcMediaChannel : public T, public Transport { - public: - RtcMediaChannel(E *engine, int channel) - : engine_(engine), audio_channel_(channel), sequence_number_(-1) {} - E *engine() { return engine_; } - int audio_channel() const { return audio_channel_; } - bool valid() const { return audio_channel_ != -1; } - protected: - // implements Transport interface - virtual int SendPacket(int channel, const void *data, int len) { - if (!T::network_interface_) { - return -1; - } - - const uint8* header = static_cast(data); - sequence_number_ = talk_base::GetBE16(header + 2); - - talk_base::Buffer packet(data, len, cricket::kMaxRtpPacketLen); - return T::network_interface_->SendPacket(&packet) ? len : -1; - } - virtual int SendRTCPPacket(int channel, const void *data, int len) { - if (!T::network_interface_) { - return -1; - } - - talk_base::Buffer packet(data, len, cricket::kMaxRtpPacketLen); - return T::network_interface_->SendRtcp(&packet) ? len : -1; - } - int sequence_number() { - return sequence_number_; - } - private: - E *engine_; - int audio_channel_; - int sequence_number_; -}; - -// RtcVoiceMediaChannel is an implementation of VoiceMediaChannel that uses -// WebRTC Voice Engine. -class RtcVoiceMediaChannel - : public RtcMediaChannel { - public: - explicit RtcVoiceMediaChannel(RtcVoiceEngine *engine); - virtual ~RtcVoiceMediaChannel(); - virtual bool SetOptions(int options); - virtual bool SetRecvCodecs(const std::vector &codecs); - virtual bool SetSendCodecs(const std::vector &codecs); - virtual bool SetPlayout(bool playout); - bool GetPlayout(); - virtual bool SetSend(cricket::SendFlags send); - cricket::SendFlags GetSend(); - virtual bool AddStream(uint32 ssrc); - virtual bool RemoveStream(uint32 ssrc); - virtual bool GetActiveStreams(cricket::AudioInfo::StreamList* actives); - virtual int GetOutputLevel(); - - virtual bool SetRingbackTone(const char *buf, int len); - virtual bool PlayRingbackTone(uint32 ssrc, bool play, bool loop); - virtual bool PlayRingbackTone(bool play, bool loop); - virtual bool PressDTMF(int event, bool playout); - - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet); - virtual void SetSendSsrc(uint32 id); - virtual bool SetRtcpCName(const std::string& cname); - virtual bool Mute(bool mute); - virtual bool SetRecvRtpHeaderExtensions( - const std::vector& extensions) { return false; } - virtual bool SetSendRtpHeaderExtensions( - const std::vector& extensions) { return false; } - virtual bool SetSendBandwidth(bool autobw, int bps) { return false; } - virtual bool GetStats(cricket::VoiceMediaInfo* info); - - virtual void GetLastMediaError(uint32* ssrc, - VoiceMediaChannel::Error* error); - bool FindSsrc(int channel_num, uint32* ssrc); - void OnError(uint32 ssrc, int error); - virtual int GetMediaChannelId() { return audio_channel(); } - - protected: - int GetLastRtcError() { return engine()->GetLastRtcError(); } - int GetChannel(uint32 ssrc); - int GetOutputLevel(int channel); - bool EnableRtcp(int channel); - bool SetPlayout(int channel, bool playout); - static uint32 ParseSsrc(const void* data, size_t len, bool rtcp); - static Error WebRTCErrorToChannelError(int err_code); - - private: - - typedef std::map ChannelMap; - int channel_options_; - bool playout_; - cricket::SendFlags send_; - ChannelMap mux_channels_; // for multiple sources - // mux_channels_ can be read from WebRTC callback thread. Accesses off the - // WebRTC thread must be synchronized with edits on the worker thread. Reads - // on the worker thread are ok. - mutable talk_base::CriticalSection mux_channels_cs_; -}; - -} // namespace webrtc - -#endif // TALK_APP_WEBRTC_AUDIOMEDIAENGINE_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc_json_unittest.cc b/third_party_mods/libjingle/source/talk/app/webrtc/local_stream_dev.cc similarity index 53% rename from third_party_mods/libjingle/source/talk/app/webrtc_json_unittest.cc rename to third_party_mods/libjingle/source/talk/app/webrtc/local_stream_dev.cc index 93aa972f0..9b7b3a36d 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc_json_unittest.cc +++ b/third_party_mods/libjingle/source/talk/app/webrtc/local_stream_dev.cc @@ -24,54 +24,48 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#include -#include - -#include "talk/base/gunit.h" -#include "talk/app/webrtc_json.h" +#include "talk/app/webrtc/local_stream_dev.h" namespace webrtc { -Json::Value JsonValueFromString(const std::string &json) { - Json::Reader reader; - Json::Value value; - - EXPECT_TRUE(reader.parse(json, value, false)); - - return value; +scoped_refptr LocalStream::Create(const std::string& label) { + // To instantiate LocalStream use + RefCountImpl* stream = new RefCountImpl(label); + return stream; } -class WebRTCJsonTest : public testing::Test { - public: - WebRTCJsonTest() {} - ~WebRTCJsonTest() {} -}; - -TEST_F(WebRTCJsonTest, TestParseConfig) { - Json::Value value(JsonValueFromString( - "\{" - " \"connectionmediator\": \"https://somewhere.example.com/conneg\"," - " \"stun_service\": { " - " \"host\" : \"stun.service.example.com\"," - " \"service\" : \"stun\"," - " \"protocol\" : \"udp\"" - " }" - " }")); - - std::string c; - EXPECT_TRUE(GetConnectionMediator(value, c)); - std::cout << " --- connectionmediator --- : " << c << std::endl; - - StunServiceDetails stun; - EXPECT_TRUE(GetStunServer(value, stun)); - std::cout << " --- stun host --- : " << stun.host << std::endl; - std::cout << " --- stun service --- : " << stun.service << std::endl; - std::cout << " --- stun protocol --- : " << stun.protocol << std::endl; +LocalStreamImpl::LocalStreamImpl(const std::string& label) + : label_(label), + ready_state_(kInitializing) { } -TEST_F(WebRTCJsonTest, TestLocalBlob) { - EXPECT_TRUE(FromSessionDescriptionToJson()); +// Implement MediaStream +const std::string& LocalStreamImpl::label() { + return label_; } +scoped_refptr LocalStreamImpl::tracks() { + return this; } + +MediaStream::ReadyState LocalStreamImpl::readyState() { + return ready_state_; +} + +// Implement MediaStreamTrackList. +size_t LocalStreamImpl::count() { + return tracks_.size(); +} + +scoped_refptr LocalStreamImpl::at(size_t index) { + return tracks_[index]; +} + +bool LocalStreamImpl::AddTrack(MediaStreamTrack* track) { + if(ready_state_ != kInitializing) + return false; + + tracks_.push_back(track); +} + +} // namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/local_stream_dev.h b/third_party_mods/libjingle/source/talk/app/webrtc/local_stream_dev.h new file mode 100644 index 000000000..a51f9fed6 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/local_stream_dev.h @@ -0,0 +1,75 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_APP_WEBRTC_LOCAL_STREAM_H_ +#define TALK_APP_WEBRTC_LOCAL_STREAM_H_ + +#include + +#include "talk/app/webrtc/notifier_impl.h" +#include "talk/app/webrtc/ref_count.h" +#include "talk/app/webrtc/stream_dev.h" +#include "talk/app/webrtc/scoped_refptr.h" + + +namespace webrtc { + +///////////////////////////////////////////// +// Local streams are Created by the PeerConnections client and provided to a +// PeerConnection object using the call PeerConnection::AddStream. + +class LocalStreamImpl + : public LocalStream, + public MediaStreamTrackList { +public: + +// static LocalStream* Create(const std::string& label); + + // Implement LocalStream. + virtual bool AddTrack(MediaStreamTrack* track); + + // Implement MediaStream + virtual const std::string& label(); + virtual scoped_refptr tracks(); + virtual ReadyState readyState(); + + // Implement MediaStreamTrackList. + virtual size_t count(); + virtual scoped_refptr at(size_t index); + + protected: + LocalStreamImpl(const std::string& label); + std::string label_; + ReadyState ready_state_; + std::vector > tracks_; +}; + + + +} // namespace webrtc + +#endif // TALK_APP_WEBRTC_LOCAL_STREAM_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/notifier_impl.h b/third_party_mods/libjingle/source/talk/app/webrtc/notifier_impl.h new file mode 100644 index 000000000..5b9abd1a7 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/notifier_impl.h @@ -0,0 +1,36 @@ +#ifndef TALK_APP_WEBRTC_NOTIFIER_IMPL_H_ +#define TALK_APP_WEBRTC_NOTIFIER_IMPL_H_ + +// Implement a template version of a notifier. +// TODO - allow multiple observers. +//#include + +#include "talk/app/webrtc/stream_dev.h" + +namespace webrtc { +template +class NotifierImpl : public T{ +public: + NotifierImpl(){ + } + + virtual void RegisterObserver(Observer* observer) { + observer_ = observer; + } + + virtual void UnregisterObserver(Observer*) { + observer_ = NULL; + } + + void FireOnChanged() { + if(observer_) + observer_->OnChanged(); + } + +protected: + Observer* observer_; +}; + +} // namespace webrtc + +#endif // TALK_APP_WEBRTC_REF_COUNT_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.cc b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.cc new file mode 100644 index 000000000..84701e8e9 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.cc @@ -0,0 +1,47 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/app/webrtc/peerconnection_impl.h" + +namespace webrtc { + +PeerConnection* PeerConnection::Create(const std::string& config, + cricket::PortAllocator* port_allocator, + cricket::MediaEngine* media_engine, + talk_base::Thread* worker_thread, + cricket::DeviceManager* device_manager) { + return new PeerConnectionImpl(config, port_allocator, media_engine, + worker_thread, device_manager); +} + +PeerConnection* PeerConnection::Create(const std::string& config, + cricket::PortAllocator* port_allocator, + talk_base::Thread* worker_thread) { + return new PeerConnectionImpl(config, port_allocator, worker_thread); +} + +} // namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.h b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.h new file mode 100644 index 000000000..f3404d016 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection.h @@ -0,0 +1,142 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_APP_WEBRTC_PEERCONNECTION_H_ +#define TALK_APP_WEBRTC_PEERCONNECTION_H_ + +// TODO(mallinath) - Add a factory class or some kind of PeerConnection manager +// to support multiple PeerConnection object instantiation. This class will +// create ChannelManager object and pass it to PeerConnection object. Otherwise +// each PeerConnection object will have its own ChannelManager hence MediaEngine +// and VoiceEngine/VideoEngine. + +#include + +namespace cricket { +class DeviceManager; +class MediaEngine; +class PortAllocator; +class VideoRenderer; +} + +namespace talk_base { +class Thread; +} + +namespace webrtc { + +class PeerConnectionObserver { + public: + virtual void OnInitialized() = 0; + virtual void OnError() = 0; + + // serialized signaling message + virtual void OnSignalingMessage(const std::string& msg) = 0; + + // Triggered when a local stream has been added and initialized + virtual void OnLocalStreamInitialized(const std::string& stream_id, + bool video) = 0; + + // Triggered when a remote peer accepts a media connection. + virtual void OnAddStream(const std::string& stream_id, bool video) = 0; + + // Triggered when a remote peer closes a media stream. + virtual void OnRemoveStream(const std::string& stream_id, bool video) = 0; + + protected: + // Dtor protected as objects shouldn't be deleted via this interface. + ~PeerConnectionObserver() {} +}; + +class PeerConnection { + public: + virtual ~PeerConnection() {} + static PeerConnection* Create(const std::string& config, + cricket::PortAllocator* port_allocator, + cricket::MediaEngine* media_engine, + talk_base::Thread* worker_thread, + cricket::DeviceManager* device_manager); + static PeerConnection* Create(const std::string& config, + cricket::PortAllocator* port_allocator, + talk_base::Thread* worker_thread); + + // Initialize + virtual bool Init() = 0; + + // Register a listener + virtual void RegisterObserver(PeerConnectionObserver* observer) = 0; + + // SignalingMessage in json format + virtual bool SignalingMessage(const std::string& msg) = 0; + + // Asynchronously adds a local stream device to the peer + // connection. The operation is complete when + // PeerConnectionObserver::OnLocalStreamInitialized is called. + virtual bool AddStream(const std::string& stream_id, bool video) = 0; + + // Asynchronously removes a local stream device from the peer + // connection. The operation is complete when + // PeerConnectionObserver::OnRemoveStream is called. + virtual bool RemoveStream(const std::string& stream_id) = 0; + + // Info the peerconnection that it is time to return the signaling + // information. The operation is complete when + // PeerConnectionObserver::OnSignalingMessage is called. + virtual bool Connect() = 0; + + // Remove all the streams and tear down the session. + // After the Close() is called, the OnSignalingMessage will be invoked + // asynchronously. And before OnSignalingMessage is called, + // OnRemoveStream will be called for each stream that was active. + // TODO(ronghuawu): Add an event such as onclose, or onreadystatechanged + // when the readystate reaches the closed state (no more streams in the + // peerconnection object. + virtual void Close() = 0; + + // Set the audio input & output devices based on the given device name. + // An empty device name means to use the default audio device. + virtual bool SetAudioDevice(const std::string& wave_in_device, + const std::string& wave_out_device, + int opts) = 0; + + // Set the video renderer for the camera preview. + virtual bool SetLocalVideoRenderer(cricket::VideoRenderer* renderer) = 0; + + // Set the video renderer for the specified stream. + virtual bool SetVideoRenderer(const std::string& stream_id, + cricket::VideoRenderer* renderer) = 0; + + // Set video capture device + // For Chromium the cam_device should use the capture session id. + // For standalone app, cam_device is the camera name. It will try to + // set the default capture device when cam_device is "". + virtual bool SetVideoCapture(const std::string& cam_device) = 0; +}; + +} // namespace webrtc + +#endif // TALK_APP_WEBRTC_PEERCONNECTION_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtcsession.cc b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_dev.cc similarity index 75% rename from third_party_mods/libjingle/source/talk/app/webrtcsession.cc rename to third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_dev.cc index 895b665b6..77dc1f6e5 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtcsession.cc +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_dev.cc @@ -25,15 +25,20 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "talk/app/webrtcsession.h" +#include "talk/app/webrtc/peerconnection_impl.h" namespace webrtc { +PeerConnection* PeerConnection::Create( + const std::string& config, + cricket::PortAllocator* port_allocator, + talk_base::Thread* worker_thread, + cricket::DeviceManager* device_manager) { + return new PeerConnectionImpl(config, port_allocator, + worker_thread, device_manager); +} -const std::string WebRTCSession::kOutgoingDirection = "s"; -const std::string WebRTCSession::kIncomingDirection = "r"; -//const std::string WebRTCSession::kAudioType = "a"; -//const std::string WebRTCSession::kVideoType = "v"; -//const std::string WebRTCSession::kTestType = "t"; - -} /* namespace webrtc */ +PeerConnection* PeerConnection::Create(const std::string& config) { + return new PeerConnectionImpl(config); +} +} // namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_dev.h b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_dev.h new file mode 100644 index 000000000..b80eb0da9 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_dev.h @@ -0,0 +1,112 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_APP_WEBRTC_PEERCONNECTION_H_ +#define TALK_APP_WEBRTC_PEERCONNECTION_H_ + +#include + +#include "talk/app/webrtc/stream_dev.h" + +namespace cricket { +class PortAllocator; +class DeviceManager; +} + +namespace talk_base { +class Thread; +} + +namespace webrtc { + +///////////////////////////////////////////// +class PeerConnectionObserver { + public: + enum Readiness { + kNegotiating, + kActive, + }; + + virtual void OnError() = 0; + + virtual void OnMessage(const std::string& msg) = 0; + + // serialized signaling message + // First message will be the initial offer. + virtual void OnSignalingMessage(const std::string& msg) = 0; + + virtual void OnStateChange(Readiness state) = 0; + + // Triggered when media is received on a new stream from remote peer. + // The label is unique for a certain peer_id. + virtual void OnAddStream(scoped_refptr stream) = 0; + + // Triggered when a remote peer close a stream. + virtual void OnRemoveStream(scoped_refptr stream) = 0; + + protected: + // Dtor protected as objects shouldn't be deleted via this interface. + ~PeerConnectionObserver() {} +}; + +class StreamCollection : public RefCount { + public: + virtual size_t count() = 0; + virtual MediaStream* at(size_t index) = 0; +}; + +class PeerConnection { + public: + // Start Negotiation. Negotiation is based on if + // SignalingMessage and AddStream have been called prior to this function. + virtual bool StartNegotiation() = 0; + + // SignalingMessage in json format + virtual bool SignalingMessage(const std::string& msg) = 0; + + // Sends the msg over a data stream. + virtual bool Send(const std::string& msg) = 0; + + // Accessor methods to active local streams. + virtual scoped_refptr local_streams() = 0; + + // Accessor methods to remote streams. + virtual scoped_refptr remote_streams() = 0; + + // Add a new local stream. + virtual void AddStream(LocalStream* stream) = 0; + + // Remove a local stream and stop sending it. + virtual void RemoveStream(LocalStream* stream) = 0; + + virtual ~PeerConnection(){}; + +}; + +} // namespace webrtc + +#endif // TALK_APP_WEBRTC_PEERCONNECTION_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.cc b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.cc new file mode 100644 index 000000000..82c27b3b4 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.cc @@ -0,0 +1,615 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "talk/app/webrtc/peerconnection_impl.h" + +#include "talk/base/basicpacketsocketfactory.h" +#include "talk/base/helpers.h" +#include "talk/base/stringencode.h" +#include "talk/base/logging.h" +#include "talk/p2p/client/basicportallocator.h" + +#include "talk/app/webrtc/webrtcsession.h" +#include "talk/app/webrtc/webrtc_json.h" + +using talk_base::ThreadManager; + +namespace webrtc { + +// The number of the tokens in the config string. +static const size_t kConfigTokens = 2; + +// The default stun port. +static const int kDefaultStunPort = 3478; + +enum { + MSG_WEBRTC_ADDSTREAM = 1, + MSG_WEBRTC_REMOVESTREAM, + MSG_WEBRTC_SIGNALINGMESSAGE, + MSG_WEBRTC_SETAUDIODEVICE, + MSG_WEBRTC_SETLOCALRENDERER, + MSG_WEBRTC_SETVIDEORENDERER, + MSG_WEBRTC_SETVIDEOCAPTURE, + MSG_WEBRTC_CONNECT, + MSG_WEBRTC_CLOSE, + MSG_WEBRTC_INIT_CHANNELMANAGER, + MSG_WEBRTC_RELEASE, +}; + +struct AddStreamParams : public talk_base::MessageData { + AddStreamParams(const std::string& stream_id, bool video) + : stream_id(stream_id), video(video) {} + + std::string stream_id; + bool video; + bool result; +}; + +struct RemoveStreamParams : public talk_base::MessageData { + explicit RemoveStreamParams(const std::string& stream_id) + : stream_id(stream_id) {} + + std::string stream_id; + bool result; +}; + +struct SignalingMsgParams : public talk_base::MessageData { + explicit SignalingMsgParams(const std::string& signaling_message) + : signaling_message(signaling_message) {} + + std::string signaling_message; + bool result; +}; + +struct SetAudioDeviceParams : public talk_base::MessageData { + SetAudioDeviceParams(const std::string& wave_in_device, + const std::string& wave_out_device, + int opts) + : wave_in_device(wave_in_device), wave_out_device(wave_out_device), + opts(opts) {} + + std::string wave_in_device; + std::string wave_out_device; + int opts; + bool result; +}; + +struct SetLocalRendererParams : public talk_base::MessageData { + explicit SetLocalRendererParams(cricket::VideoRenderer* renderer) + : renderer(renderer) {} + + cricket::VideoRenderer* renderer; + bool result; +}; + +struct SetVideoRendererParams : public talk_base::MessageData { + SetVideoRendererParams(const std::string& stream_id, + cricket::VideoRenderer* renderer) + : stream_id(stream_id), renderer(renderer) {} + + std::string stream_id; + cricket::VideoRenderer* renderer; + bool result; +}; + +struct SetVideoCaptureParams : public talk_base::MessageData { + explicit SetVideoCaptureParams(const std::string& cam_device) + : cam_device(cam_device) {} + + std::string cam_device; + bool result; +}; + +struct ConnectParams : public talk_base::MessageData { + bool result; +}; + +PeerConnectionImpl::PeerConnectionImpl(const std::string& config, + cricket::PortAllocator* port_allocator, + cricket::MediaEngine* media_engine, + talk_base::Thread* worker_thread, + cricket::DeviceManager* device_manager) + : config_(config), + port_allocator_(port_allocator), + media_engine_(media_engine), + worker_thread_(worker_thread), + device_manager_(device_manager), + signaling_thread_(NULL), + initialized_(false), + service_type_(SERVICE_COUNT), + event_callback_(NULL), + session_(NULL), + incoming_(false) { +} + +PeerConnectionImpl::PeerConnectionImpl(const std::string& config, + cricket::PortAllocator* port_allocator, + talk_base::Thread* worker_thread) + : config_(config), + port_allocator_(port_allocator), + media_engine_(NULL), + worker_thread_(worker_thread), + device_manager_(NULL), + signaling_thread_(NULL), + initialized_(false), + service_type_(SERVICE_COUNT), + event_callback_(NULL), + session_(NULL), + incoming_(false) { +} + +PeerConnectionImpl::~PeerConnectionImpl() { + signaling_thread_->Send(this, MSG_WEBRTC_RELEASE); +} + +void PeerConnectionImpl::Release_s() { + session_.reset(); + channel_manager_.reset(); +} + +bool PeerConnectionImpl::Init() { + ASSERT(!initialized_); + std::vector stun_hosts; + talk_base::SocketAddress stun_addr; + if (!ParseConfigString(config_, &stun_addr)) + return false; + stun_hosts.push_back(stun_addr); + + signaling_thread_.reset(new talk_base::Thread()); + ASSERT(signaling_thread_.get()); + if (!signaling_thread_->SetName("signaling thread", this) || + !signaling_thread_->Start()) { + LOG(WARNING) << "Failed to start libjingle signaling thread"; + return false; + } + + signaling_thread_->Post(this, MSG_WEBRTC_INIT_CHANNELMANAGER); + return true; +} + +bool PeerConnectionImpl::ParseConfigString( + const std::string& config, talk_base::SocketAddress* stun_addr) { + std::vector tokens; + talk_base::tokenize(config_, ' ', &tokens); + + if (tokens.size() != kConfigTokens) { + LOG(WARNING) << "Invalid config string"; + return false; + } + + service_type_ = SERVICE_COUNT; + // NOTE: Must be in the same order as the enum. + static const char* kValidServiceTypes[SERVICE_COUNT] = { + "STUN", "STUNS", "TURN", "TURNS" + }; + const std::string& type = tokens[0]; + for (size_t i = 0; i < SERVICE_COUNT; ++i) { + if (type.compare(kValidServiceTypes[i]) == 0) { + service_type_ = static_cast(i); + break; + } + } + + if (service_type_ == SERVICE_COUNT) { + LOG(WARNING) << "Invalid service type: " << type; + return false; + } + std::string service_address = tokens[1]; + + int port; + tokens.clear(); + talk_base::tokenize(service_address, ':', &tokens); + if (tokens.size() != kConfigTokens) { + port = kDefaultStunPort; + } else { + port = atoi(tokens[1].c_str()); + if (port <= 0 || port > 0xffff) { + LOG(WARNING) << "Invalid port: " << tokens[1]; + return false; + } + } + stun_addr->SetIP(service_address); + stun_addr->SetPort(port); + return true; +} + +void PeerConnectionImpl::RegisterObserver(PeerConnectionObserver* observer) { + // This assert is to catch cases where two observer pointers are registered. + // We only support one and if another is to be used, the current one must be + // cleared first. + ASSERT(observer == NULL || event_callback_ == NULL); + event_callback_ = observer; +} + +bool PeerConnectionImpl::SignalingMessage( + const std::string& signaling_message) { + SignalingMsgParams* msg = new SignalingMsgParams(signaling_message); + signaling_thread_->Post(this, MSG_WEBRTC_SIGNALINGMESSAGE, msg); + return true; +} + +bool PeerConnectionImpl::SignalingMessage_s(const std::string& msg) { + // Deserialize signaling message + cricket::SessionDescription* incoming_sdp = NULL; + std::vector candidates; + if (!ParseJSONSignalingMessage(msg, incoming_sdp, &candidates)) { + if (event_callback_) + event_callback_->OnError(); + return false; + } + + bool ret = false; + if (!session_.get()) { + // this will be incoming call + std::string sid; + talk_base::CreateRandomString(8, &sid); + std::string direction("r"); + session_.reset(CreateMediaSession(sid, direction)); + ASSERT(session_.get() != NULL); + incoming_ = true; + ret = session_->OnInitiateMessage(incoming_sdp, candidates); + } else { + ret = session_->OnRemoteDescription(incoming_sdp, candidates); + } + + if (!ret && event_callback_) + event_callback_->OnError(); + + return ret; +} + +WebRTCSession* PeerConnectionImpl::CreateMediaSession( + const std::string& id, const std::string& dir) { + ASSERT(port_allocator_ != NULL); + WebRTCSession* session = new WebRTCSession(id, dir, + port_allocator_, channel_manager_.get(), + signaling_thread_.get()); + + if (session->Initiate()) { + session->SignalRemoveStream.connect( + this, + &PeerConnectionImpl::SendRemoveSignal); + session->SignalAddStream.connect( + this, + &PeerConnectionImpl::OnAddStream); + session->SignalRemoveStream2.connect( + this, + &PeerConnectionImpl::OnRemoveStream2); + session->SignalRtcMediaChannelCreated.connect( + this, + &PeerConnectionImpl::OnRtcMediaChannelCreated); + session->SignalLocalDescription.connect( + this, + &PeerConnectionImpl::OnLocalDescription); + session->SignalFailedCall.connect( + this, + &PeerConnectionImpl::OnFailedCall); + } else { + delete session; + session = NULL; + } + return session; +} + +void PeerConnectionImpl::SendRemoveSignal(WebRTCSession* session) { + if (event_callback_) { + std::string message; + if (GetJSONSignalingMessage(session->remote_description(), + session->local_candidates(), &message)) { + event_callback_->OnSignalingMessage(message); + // TODO(ronghuawu): Notify the client when the PeerConnection object + // doesn't have any streams. Something like the onreadystatechanged + // + setting readyState to 'CLOSED'. + } + } +} + +bool PeerConnectionImpl::AddStream(const std::string& stream_id, bool video) { + AddStreamParams* params = new AddStreamParams(stream_id, video); + signaling_thread_->Post(this, MSG_WEBRTC_ADDSTREAM, params, true); + return true; +} + +bool PeerConnectionImpl::AddStream_s(const std::string& stream_id, bool video) { + if (!session_.get()) { + // if session doesn't exist then this should be an outgoing call + std::string sid; + talk_base::CreateRandomString(8, &sid); + session_.reset(CreateMediaSession(sid, "s")); + if (session_.get() == NULL) { + ASSERT(false && "failed to initialize a session"); + return false; + } + } + + bool ret = false; + if (session_->HasStream(stream_id)) { + ASSERT(false && "A stream with this name already exists"); + } else { + if (!video) { + ret = !session_->HasAudioStream() && + session_->CreateVoiceChannel(stream_id); + } else { + ret = !session_->HasVideoStream() && + session_->CreateVideoChannel(stream_id); + } + } + return ret; +} + +bool PeerConnectionImpl::RemoveStream(const std::string& stream_id) { + RemoveStreamParams* params = new RemoveStreamParams(stream_id); + signaling_thread_->Post(this, MSG_WEBRTC_REMOVESTREAM, params); + return true; +} + +bool PeerConnectionImpl::RemoveStream_s(const std::string& stream_id) { + if (!session_.get()) { + if (event_callback_) { + event_callback_->OnError(); + } + return false; + } + return session_->RemoveStream(stream_id); +} + +void PeerConnectionImpl::OnLocalDescription( + const cricket::SessionDescription* desc, + const std::vector& candidates) { + if (!desc) { + LOG(WARNING) << "no local SDP "; + return; + } + + std::string message; + if (GetJSONSignalingMessage(desc, candidates, &message)) { + if (event_callback_) { + event_callback_->OnSignalingMessage(message); + } + } +} + +void PeerConnectionImpl::OnFailedCall() { + // TODO(mallinath): implement. +} + +bool PeerConnectionImpl::SetAudioDevice(const std::string& wave_in_device, + const std::string& wave_out_device, + int opts) { + SetAudioDeviceParams* params = new SetAudioDeviceParams(wave_in_device, + wave_out_device, opts); + signaling_thread_->Post(this, MSG_WEBRTC_SETAUDIODEVICE, params); + return true; +} + +bool PeerConnectionImpl::SetAudioDevice_s(const std::string& wave_in_device, + const std::string& wave_out_device, + int opts) { + return channel_manager_->SetAudioOptions(wave_in_device, + wave_out_device, + opts); +} + +bool PeerConnectionImpl::SetLocalVideoRenderer( + cricket::VideoRenderer* renderer) { + SetLocalRendererParams* params = new SetLocalRendererParams(renderer); + signaling_thread_->Post(this, MSG_WEBRTC_SETLOCALRENDERER, params); + return true; +} + +bool PeerConnectionImpl::SetLocalVideoRenderer_s( + cricket::VideoRenderer* renderer) { + return channel_manager_->SetLocalRenderer(renderer); +} + +bool PeerConnectionImpl::SetVideoRenderer(const std::string& stream_id, + cricket::VideoRenderer* renderer) { + SetVideoRendererParams* params = new SetVideoRendererParams(stream_id, + renderer); + signaling_thread_->Post(this, MSG_WEBRTC_SETVIDEORENDERER, params); + return true; +} + +bool PeerConnectionImpl::SetVideoRenderer_s(const std::string& stream_id, + cricket::VideoRenderer* renderer) { + if (!session_.get()) { + if (event_callback_) { + event_callback_->OnError(); + } + return false; + } + return session_->SetVideoRenderer(stream_id, renderer); +} + +bool PeerConnectionImpl::SetVideoCapture(const std::string& cam_device) { + SetVideoCaptureParams* params = new SetVideoCaptureParams(cam_device); + signaling_thread_->Post(this, MSG_WEBRTC_SETVIDEOCAPTURE, params); + return true; +} + +bool PeerConnectionImpl::SetVideoCapture_s(const std::string& cam_device) { + return channel_manager_->SetVideoOptions(cam_device); +} + +bool PeerConnectionImpl::Connect() { + signaling_thread_->Post(this, MSG_WEBRTC_CONNECT); + return true; +} + +bool PeerConnectionImpl::Connect_s() { + if (!session_.get()) { + if (event_callback_) { + event_callback_->OnError(); + } + return false; + } + + return session_->Connect(); +} + +void PeerConnectionImpl::OnAddStream(const std::string& stream_id, + bool video) { + if (event_callback_) { + event_callback_->OnAddStream(stream_id, video); + } +} + +void PeerConnectionImpl::OnRemoveStream2(const std::string& stream_id, + bool video) { + if (event_callback_) { + event_callback_->OnRemoveStream(stream_id, video); + } +} + +void PeerConnectionImpl::OnRtcMediaChannelCreated(const std::string& stream_id, + bool video) { + if (event_callback_) { + event_callback_->OnLocalStreamInitialized(stream_id, video); + } +} + +void PeerConnectionImpl::CreateChannelManager_s() { + // create cricket::ChannelManager object + ASSERT(worker_thread_ != NULL); + if (media_engine_ && device_manager_) { + channel_manager_.reset(new cricket::ChannelManager( + media_engine_, device_manager_, worker_thread_)); + } else { + channel_manager_.reset(new cricket::ChannelManager(worker_thread_)); + } + + initialized_ = channel_manager_->Init(); + + if (event_callback_) { + if (initialized_) + event_callback_->OnInitialized(); + else + event_callback_->OnError(); + } +} + +void PeerConnectionImpl::Close() { + signaling_thread_->Post(this, MSG_WEBRTC_CLOSE); +} + +void PeerConnectionImpl::Close_s() { + if (!session_.get()) { + if (event_callback_) + event_callback_->OnError(); + return; + } + + session_->RemoveAllStreams(); +} + +void PeerConnectionImpl::OnMessage(talk_base::Message* message) { + talk_base::MessageData* data = message->pdata; + switch (message->message_id) { + case MSG_WEBRTC_ADDSTREAM: { + AddStreamParams* params = reinterpret_cast(data); + params->result = AddStream_s(params->stream_id, params->video); + delete params; + break; + } + case MSG_WEBRTC_SIGNALINGMESSAGE: { + SignalingMsgParams* params = + reinterpret_cast(data); + params->result = SignalingMessage_s(params->signaling_message); + if (!params->result && event_callback_) + event_callback_->OnError(); + delete params; + break; + } + case MSG_WEBRTC_REMOVESTREAM: { + RemoveStreamParams* params = reinterpret_cast(data); + params->result = RemoveStream_s(params->stream_id); + delete params; + break; + } + case MSG_WEBRTC_SETAUDIODEVICE: { + SetAudioDeviceParams* params = + reinterpret_cast(data); + params->result = SetAudioDevice_s( + params->wave_in_device, params->wave_out_device, params->opts); + if (!params->result && event_callback_) + event_callback_->OnError(); + delete params; + break; + } + case MSG_WEBRTC_SETLOCALRENDERER: { + SetLocalRendererParams* params = + reinterpret_cast(data); + params->result = SetLocalVideoRenderer_s(params->renderer); + if (!params->result && event_callback_) + event_callback_->OnError(); + delete params; + break; + } + case MSG_WEBRTC_SETVIDEOCAPTURE: { + SetVideoCaptureParams* params = + reinterpret_cast(data); + params->result = SetVideoCapture_s(params->cam_device); + if (!params->result && event_callback_) + event_callback_->OnError(); + delete params; + break; + } + case MSG_WEBRTC_SETVIDEORENDERER: { + SetVideoRendererParams* params = + reinterpret_cast(data); + params->result = SetVideoRenderer_s(params->stream_id, params->renderer); + if (!params->result && event_callback_) + event_callback_->OnError(); + delete params; + break; + } + case MSG_WEBRTC_CONNECT: { + Connect_s(); + break; + } + case MSG_WEBRTC_CLOSE: { + Close_s(); + break; + } + case MSG_WEBRTC_INIT_CHANNELMANAGER: { + CreateChannelManager_s(); + break; + } + case MSG_WEBRTC_RELEASE: { + Release_s(); + break; + } + default: { + ASSERT(false); + break; + } + } +} + +} // namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.h b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.h new file mode 100644 index 000000000..8b7f310d7 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/peerconnection_impl.h @@ -0,0 +1,169 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_APP_WEBRTC_PEERCONNECTION_IMPL_H_ +#define TALK_APP_WEBRTC_PEERCONNECTION_IMPL_H_ + +#include "talk/app/webrtc/peerconnection.h" + +#include +#include "talk/base/sigslot.h" +#include "talk/base/scoped_ptr.h" +#include "talk/base/packetsocketfactory.h" +#include "talk/base/thread.h" +#include "talk/session/phone/channelmanager.h" + +namespace Json { +class Value; +} + +namespace cricket { +class BasicPortAllocator; +class ChannelManager; +class DeviceManager; +class SessionDescription; +} + +namespace webrtc { + +class AudioDeviceModule; +class ExternalRenderer; +class WebRTCSession; + +class PeerConnectionImpl : public PeerConnection, + public talk_base::MessageHandler, + public sigslot::has_slots<> { + public: + PeerConnectionImpl(const std::string& config, + cricket::PortAllocator* port_allocator, + cricket::MediaEngine* media_engine, + talk_base::Thread* worker_thread, + cricket::DeviceManager* device_manager); + PeerConnectionImpl(const std::string& config, + cricket::PortAllocator* port_allocator, + talk_base::Thread* worker_thread); + virtual ~PeerConnectionImpl(); + + enum ReadyState { + NEW = 0, + NEGOTIATING, + ACTIVE, + CLOSED, + }; + + // PeerConnection interfaces + bool Init(); + void RegisterObserver(PeerConnectionObserver* observer); + bool SignalingMessage(const std::string& msg); + bool AddStream(const std::string& stream_id, bool video); + bool RemoveStream(const std::string& stream_id); + bool Connect(); + void Close(); + bool SetAudioDevice(const std::string& wave_in_device, + const std::string& wave_out_device, int opts); + bool SetLocalVideoRenderer(cricket::VideoRenderer* renderer); + bool SetVideoRenderer(const std::string& stream_id, + cricket::VideoRenderer* renderer); + bool SetVideoCapture(const std::string& cam_device); + + // Access to the members + const std::string& config() const { return config_; } + bool incoming() const { return incoming_; } + cricket::ChannelManager* channel_manager() { + return channel_manager_.get(); + } + ReadyState ready_state() const { return ready_state_; } + + // Callbacks from PeerConnectionImplCallbacks + void OnAddStream(const std::string& stream_id, bool video); + void OnRemoveStream2(const std::string& stream_id, bool video); + void OnLocalDescription( + const cricket::SessionDescription* desc, + const std::vector& candidates); + void OnFailedCall(); + void OnRtcMediaChannelCreated(const std::string& stream_id, + bool video); + + private: + bool ParseConfigString(const std::string& config, + talk_base::SocketAddress* stun_addr); + void WrapChromiumThread(); + void SendRemoveSignal(WebRTCSession* session); + WebRTCSession* CreateMediaSession(const std::string& id, + const std::string& dir); + + virtual void OnMessage(talk_base::Message* message); + + // signaling thread methods + bool AddStream_s(const std::string& stream_id, bool video); + bool SignalingMessage_s(const std::string& signaling_message); + bool RemoveStream_s(const std::string& stream_id); + bool Connect_s(); + void Close_s(); + bool SetAudioDevice_s(const std::string& wave_in_device, + const std::string& wave_out_device, int opts); + bool SetLocalVideoRenderer_s(cricket::VideoRenderer* renderer); + bool SetVideoRenderer_s(const std::string& stream_id, + cricket::VideoRenderer* renderer); + bool SetVideoCapture_s(const std::string& cam_device); + void CreateChannelManager_s(); + void Release_s(); + + std::string config_; + talk_base::scoped_ptr channel_manager_; + cricket::PortAllocator* port_allocator_; + cricket::MediaEngine* media_engine_; + talk_base::Thread* worker_thread_; + cricket::DeviceManager* device_manager_; + talk_base::scoped_ptr signaling_thread_; + + bool initialized_; + ReadyState ready_state_; + // TODO(ronghuawu/tommi): Replace the initialized_ with ready_state_. + // Fire notifications via the observer interface + // when ready_state_ changes (i.e. onReadyStateChanged()). + + // NOTE: The order of the enum values must be in sync with the array + // in Init(). + enum ServiceType { + STUN, + STUNS, + TURN, + TURNS, + SERVICE_COUNT, // Also means 'invalid'. + }; + + ServiceType service_type_; + PeerConnectionObserver* event_callback_; + talk_base::scoped_ptr session_; + // TODO(ronghua): There's no such concept as "incoming" and "outgoing" + // according to the spec. This will be removed in the new PeerConnection. + bool incoming_; +}; +} + +#endif // TALK_APP_WEBRTC_PEERCONNECTION_IMPL_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/ref_count.h b/third_party_mods/libjingle/source/talk/app/webrtc/ref_count.h new file mode 100644 index 000000000..6dca517d0 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/ref_count.h @@ -0,0 +1,43 @@ +#ifndef TALK_APP_WEBRTC_REF_COUNT_H_ +#define TALK_APP_WEBRTC_REF_COUNT_H_ + +#include + +// Reference count interface. +class RefCount { + public: + virtual size_t AddRef() = 0; + virtual size_t Release() = 0; +}; + +template +class RefCountImpl : public T { +public: + RefCountImpl() : ref_count_(0) { + } + + template + RefCountImpl(P p) : ref_count_(0), T(p) { + } + + template + RefCountImpl(P1 p1, P2 p2) : ref_count_(0), T(p1, p2) { + } + + virtual size_t AddRef() { + ++ref_count_; + return ref_count_; + } + + virtual size_t Release() { + size_t ret = --ref_count_; + if(!ref_count_) { + delete this; + } + return ret; + } +protected: + size_t ref_count_; +}; + +#endif // TALK_APP_WEBRTC_REF_COUNT_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/scoped_refptr.h b/third_party_mods/libjingle/source/talk/app/webrtc/scoped_refptr.h new file mode 100644 index 000000000..7f7c78ee4 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/scoped_refptr.h @@ -0,0 +1,128 @@ +#ifndef TALK_APP_WEBRTC_SCOPED_REFPTR_H_ +#define TALK_APP_WEBRTC_SCOPED_REFPTR_H_ +// Originally these classes are copied from Chromium. + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// }; +// +// void some_function() { +// scoped_refptr foo = new MyFoo(); +// foo->Method(param); +// // |foo| is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = new MyFoo(); +// ... +// foo = NULL; // explicitly releases |foo| +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b.swap(a); +// // now, |b| references the MyFoo object, and |a| references NULL. +// } +// +// To make both |a| and |b| in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b = a; +// // now, |a| and |b| each own a reference to the same MyFoo object. +// } +// +template +class scoped_refptr { + public: + scoped_refptr() : ptr_(NULL) { + } + + scoped_refptr(T* p) : ptr_(p) { + if (ptr_) + ptr_->AddRef(); + } + + scoped_refptr(const scoped_refptr& r) : ptr_(r.ptr_) { + if (ptr_) + ptr_->AddRef(); + } + + template + scoped_refptr(const scoped_refptr& r) : ptr_(r.get()) { + if (ptr_) + ptr_->AddRef(); + } + + ~scoped_refptr() { + if (ptr_) + ptr_->Release(); + } + + T* get() const { return ptr_; } + operator T*() const { return ptr_; } + T* operator->() const { return ptr_; } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + T* release() { + T* retVal = ptr_; + ptr_ = NULL; + return retVal; + } + + scoped_refptr& operator=(T* p) { + // AddRef first so that self assignment should work + if (p) + p->AddRef(); + if (ptr_ ) + ptr_ ->Release(); + ptr_ = p; + return *this; + } + + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.ptr_; + } + + template + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.get(); + } + + void swap(T** pp) { + T* p = ptr_; + ptr_ = *pp; + *pp = p; + } + + void swap(scoped_refptr& r) { + swap(&r.ptr_); + } + + protected: + T* ptr_; +}; + +#endif // TALK_APP_WEBRTC_SCOPED_REFPTR_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/stream_dev.h b/third_party_mods/libjingle/source/talk/app/webrtc/stream_dev.h new file mode 100644 index 000000000..aab4dc9a9 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/stream_dev.h @@ -0,0 +1,213 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_APP_WEBRTC_STREAM_H_ +#define TALK_APP_WEBRTC_STREAM_H_ + +#include + +#include "talk/app/webrtc/ref_count.h" +#include "talk/app/webrtc/scoped_refptr.h" + +namespace cricket { +class VideoRenderer; +class MediaEngine; +} // namespace cricket + +namespace webrtc { + +class AudioDeviceModule; +class VideoCaptureModule; + +const char kVideoTrackKind[] = "video"; +const char kAudioTrackKind[] = "audio"; + +// Generic observer interface. +class Observer { + public: + virtual void OnChanged() = 0; +}; + +class Notifier { + virtual void RegisterObserver(Observer*) = 0; + virtual void UnregisterObserver(Observer*) = 0; + // This method should only be accessible to the owner + //void FireOnChanged() = 0; +}; + +// Information about a track. +class MediaStreamTrack : public RefCount, + public Notifier { + public: + virtual const std::string& kind() = 0; + virtual const std::string& label() = 0; + virtual bool enabled() = 0; + // Enable or disables a track. + // For Remote streams - disable means that the video is not decoded, + // or audio not decoded. + // For local streams this means that video is not captured + // or audio is not captured. + virtual bool set_enabled(bool enable); +}; + +// Reference counted wrapper for an AudioDeviceModule. +class AudioDevice : public RefCount { + public: + static scoped_refptr Create(const std::string& name, + AudioDeviceModule* adm); + + // Name of this device. Same as label of a MediaStreamTrack. + const std::string& name(); + + AudioDeviceModule* module(); + + protected: + AudioDevice(){}; + virtual ~AudioDevice() {}; + void Initialize(const std::string& name, AudioDeviceModule* adm); + + std::string name_; + AudioDeviceModule* adm_; +}; + +// Reference counted wrapper for a VideoCaptureModule. +class VideoDevice : public RefCount { + public: + static scoped_refptr Create(const std::string& name, + VideoCaptureModule* vcm); + // Name of this device. Same as label of a MediaStreamTrack. + const std::string& name(); + + VideoCaptureModule* module(); + + protected: + VideoDevice(){}; + ~VideoDevice() {}; + void Initialize(const std::string& name, VideoCaptureModule* vcm); + + std::string name_; + VideoCaptureModule* vcm_; +}; + +// Reference counted wrapper for a VideoRenderer. +class VideoRenderer : public RefCount { + public: + static scoped_refptr Create(cricket::VideoRenderer* renderer); + virtual cricket::VideoRenderer* module(); + + protected: + VideoRenderer() {}; + ~VideoRenderer() {}; + void Initialize(cricket::VideoRenderer* renderer); + + cricket::VideoRenderer* renderer_; +}; + +class VideoTrack : public MediaStreamTrack { + public: + // Set the video renderer for a local or remote stream. + // This call will start decoding the received video stream and render it. + virtual void SetRenderer(VideoRenderer* renderer) = 0; + + // Get the VideoRenderer associated with this track. + virtual scoped_refptr GetRenderer() = 0; + + protected: + virtual ~VideoTrack() {}; +}; + +class LocalVideoTrack : public VideoTrack { + public: + static scoped_refptr Create(VideoDevice* video_device); + + // Get the VideoCapture device associated with this track. + virtual scoped_refptr GetVideoCapture() = 0; + + protected: + virtual ~LocalVideoTrack() {}; +}; + +class AudioTrack : public MediaStreamTrack { + public: + protected: + virtual ~AudioTrack() {}; +}; + +class LocalAudioTrack : public AudioTrack { + public: + static scoped_refptr Create(AudioDevice* audio_device); + + // Get the AudioDevice associated with this track. + virtual scoped_refptr GetAudioDevice() = 0; + protected: + virtual ~LocalAudioTrack() {}; +}; + +// List of of tracks. +class MediaStreamTrackList : public RefCount { + public: + virtual size_t count() = 0; + virtual scoped_refptr at(size_t index) = 0; + + protected: + virtual ~MediaStreamTrackList() {}; +}; + +class MediaStream : public RefCount { + public: + virtual const std::string& label() = 0; + virtual scoped_refptr tracks() = 0; + + enum ReadyState { + kInitializing, + kLive = 1, // Stream alive + kEnded = 2, // Stream have ended + }; + + virtual ReadyState readyState() = 0; + + protected: + virtual ~MediaStream() {}; +}; + +class LocalStream : public MediaStream { +public: + static scoped_refptr Create(const std::string& label); + virtual bool AddTrack(MediaStreamTrack* track) = 0; +}; + +// Remote streams are created by the PeerConnection object and provided to the +// client using PeerConnectionObserver::OnAddStream. +// The client can provide the renderer to the PeerConnection object calling +// VideoTrack::SetRenderer. +class RemoteStream : public MediaStream { +public: +}; + +} // namespace webrtc + +#endif // TALK_APP_WEBRTC_STREAM_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/webrtc.scons b/third_party_mods/libjingle/source/talk/app/webrtc/webrtc.scons new file mode 100644 index 000000000..f4953f82d --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/webrtc.scons @@ -0,0 +1,56 @@ +# -*- Python -*- +import talk + +Import('env') + +# local sources +talk.Library( + env, + name = 'webrtc', + srcs = [ + 'peerconnection.cc', + 'peerconnection_impl.cc', + 'webrtc_json.cc', + 'webrtcsession.cc', + ], +) + +talk.Unittest( + env, + name = 'webrtc', + srcs = [ + 'webrtcsession_unittest.cc', + 'testing/timing.cc' + ], + libs = [ + 'srtp', + 'base', + 'jpeg', + 'json', + 'webrtc', + 'p2p', + 'phone', + 'xmpp', + 'xmllite', + 'yuvscaler' + ], + include_talk_media_libs = True, + mac_libs = [ + 'crypto', + 'ssl', + ], + mac_FRAMEWORKS = [ + 'Foundation', + 'IOKit', + 'QTKit', + ], + lin_libs = [ + 'rt', + 'dl', + 'sound', + 'X11', + 'Xext', + 'Xfixes', + 'Xrandr' + ], +) diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/webrtc_json.cc b/third_party_mods/libjingle/source/talk/app/webrtc/webrtc_json.cc new file mode 100644 index 000000000..4073ab988 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/webrtc_json.cc @@ -0,0 +1,447 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/app/webrtc/webrtc_json.h" + +#include +#include + +#include "talk/base/json.h" +#include "talk/base/logging.h" +#include "talk/base/stringutils.h" +#include "talk/session/phone/mediasessionclient.h" +#include "talk/session/phone/codec.h" + +namespace webrtc { +static const int kIceComponent = 1; +static const int kIceFoundation = 1; + +bool GetConnectionMediator(const Json::Value& value, + std::string* connection_mediator) { + if (value.type() != Json::objectValue && value.type() != Json::nullValue) { + LOG(LS_WARNING) << "Failed to parse stun values"; + return false; + } + + if (!GetStringFromJsonObject(value, + "connectionmediator", + connection_mediator)) { + LOG(LS_WARNING) << "Failed to parse JSON for value: " + << value.toStyledString(); + return false; + } + return true; +} + +bool GetStunServer(const Json::Value& value, StunServiceDetails* stunServer) { + if (value.type() != Json::objectValue && value.type() != Json::nullValue) { + LOG(LS_WARNING) << "Failed to parse stun values"; + return false; + } + + Json::Value stun; + if (GetValueFromJsonObject(value, "stun_service", &stun)) { + if (stun.type() == Json::objectValue) { + if (!GetStringFromJsonObject(stun, "host", &stunServer->host) || + !GetStringFromJsonObject(stun, "service", &stunServer->service) || + !GetStringFromJsonObject(stun, "protocol", &stunServer->protocol)) { + LOG(LS_WARNING) << "Failed to parse JSON value: " + << value.toStyledString(); + return false; + } + } else { + LOG(LS_WARNING) << "Failed to find the stun_service member."; + return false; + } + } else { + LOG(LS_WARNING) << "Wrong ValueType. Expect Json::objectValue)."; + return false; + } + return true; +} + +bool GetTurnServer(const Json::Value& value, std::string* turn_server) { + if (value.type() != Json::objectValue && value.type() != Json::nullValue) { + LOG(LS_WARNING) << "Failed to parse stun values"; + return false; + } + + Json::Value turn; + if (GetValueFromJsonObject(value, "turn_service", &turn)) { + if (turn.type() == Json::objectValue) { + if (!GetStringFromJsonObject(turn, "host", turn_server)) { + LOG(LS_WARNING) << "Failed to parse JSON value: " + << value.toStyledString(); + return false; + } + } else { + LOG(LS_WARNING) << "Wrong ValueType. Expect Json::objectValue)."; + return false; + } + } + return true; +} + +bool GetJSONSignalingMessage( + const cricket::SessionDescription* sdp, + const std::vector& candidates, + std::string* signaling_message) { + const cricket::ContentInfo* audio_content = GetFirstAudioContent(sdp); + const cricket::ContentInfo* video_content = GetFirstVideoContent(sdp); + + std::vector media; + if (audio_content) { + Json::Value value; + BuildMediaMessage(*audio_content, candidates, false, &value); + media.push_back(value); + } + + if (video_content) { + Json::Value value; + BuildMediaMessage(*video_content, candidates, true, &value); + media.push_back(value); + } + + Json::Value signal; + Append(&signal, "media", media); + + // now serialize + *signaling_message = Serialize(signal); + return true; +} + +bool BuildMediaMessage( + const cricket::ContentInfo& content_info, + const std::vector& candidates, + bool video, + Json::Value* params) { + if (video) { + Append(params, "label", 2); // always video 2 + } else { + Append(params, "label", 1); // always audio 1 + } + + std::vector rtpmap; + if (!BuildRtpMapParams(content_info, video, &rtpmap)) { + return false; + } + + Append(params, "rtpmap", rtpmap); + + Json::Value attributes; + std::vector jcandidates; + + if (!BuildAttributes(candidates, video, &jcandidates)) { + return false; + } + Append(&attributes, "candidate", jcandidates); + Append(params, "attributes", attributes); + return true; +} + +bool BuildRtpMapParams(const cricket::ContentInfo& content_info, + bool video, + std::vector* rtpmap) { + if (!video) { + const cricket::AudioContentDescription* audio_offer = + static_cast( + content_info.description); + + std::vector::const_iterator iter = + audio_offer->codecs().begin(); + std::vector::const_iterator iter_end = + audio_offer->codecs().end(); + for (; iter != iter_end; ++iter) { + Json::Value codec; + std::string codec_str(std::string("audio/").append(iter->name)); + // adding clockrate + Append(&codec, "clockrate", iter->clockrate); + Append(&codec, "codec", codec_str); + Json::Value codec_id; + Append(&codec_id, talk_base::ToString(iter->id), codec); + rtpmap->push_back(codec_id); + } + } else { + const cricket::VideoContentDescription* video_offer = + static_cast( + content_info.description); + + std::vector::const_iterator iter = + video_offer->codecs().begin(); + std::vector::const_iterator iter_end = + video_offer->codecs().end(); + for (; iter != iter_end; ++iter) { + Json::Value codec; + std::string codec_str(std::string("video/").append(iter->name)); + Append(&codec, "codec", codec_str); + Json::Value codec_id; + Append(&codec_id, talk_base::ToString(iter->id), codec); + rtpmap->push_back(codec_id); + } + } + return true; +} + +bool BuildAttributes(const std::vector& candidates, + bool video, + std::vector* jcandidates) { + std::vector::const_iterator iter = + candidates.begin(); + std::vector::const_iterator iter_end = + candidates.end(); + for (; iter != iter_end; ++iter) { + if ((video && !iter->name().compare("video_rtp")) || + (!video && !iter->name().compare("rtp"))) { + Json::Value candidate; + Append(&candidate, "component", kIceComponent); + Append(&candidate, "foundation", kIceFoundation); + Append(&candidate, "generation", iter->generation()); + Append(&candidate, "proto", iter->protocol()); + Append(&candidate, "priority", iter->preference()); + Append(&candidate, "ip", iter->address().IPAsString()); + Append(&candidate, "port", iter->address().PortAsString()); + Append(&candidate, "type", iter->type()); + Append(&candidate, "name", iter->name()); + Append(&candidate, "network_name", iter->network_name()); + Append(&candidate, "username", iter->username()); + Append(&candidate, "password", iter->password()); + jcandidates->push_back(candidate); + } + } + return true; +} + +std::string Serialize(const Json::Value& value) { + Json::StyledWriter writer; + return writer.write(value); +} + +bool Deserialize(const std::string& message, Json::Value* value) { + Json::Reader reader; + return reader.parse(message, *value); +} + +bool ParseJSONSignalingMessage(const std::string& signaling_message, + cricket::SessionDescription*& sdp, + std::vector* candidates) { + ASSERT(!sdp); // expect this to be NULL + // first deserialize message + Json::Value value; + if (!Deserialize(signaling_message, &value)) { + return false; + } + + // get media objects + std::vector mlines = ReadValues(value, "media"); + if (mlines.empty()) { + // no m-lines found + return false; + } + + sdp = new cricket::SessionDescription(); + + // get codec information + for (size_t i = 0; i < mlines.size(); ++i) { + if (mlines[i]["label"].asInt() == 1) { + cricket::AudioContentDescription* audio_content = + new cricket::AudioContentDescription(); + ParseAudioCodec(mlines[i], audio_content); + audio_content->SortCodecs(); + sdp->AddContent(cricket::CN_AUDIO, cricket::NS_JINGLE_RTP, audio_content); + ParseICECandidates(mlines[i], candidates); + } else { + cricket::VideoContentDescription* video_content = + new cricket::VideoContentDescription(); + ParseVideoCodec(mlines[i], video_content); + video_content->SortCodecs(); + sdp->AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP, video_content); + ParseICECandidates(mlines[i], candidates); + } + } + return true; +} + +bool ParseAudioCodec(const Json::Value& value, + cricket::AudioContentDescription* content) { + std::vector rtpmap(ReadValues(value, "rtpmap")); + if (rtpmap.empty()) + return false; + + std::vector::const_iterator iter = + rtpmap.begin(); + std::vector::const_iterator iter_end = + rtpmap.end(); + for (; iter != iter_end; ++iter) { + cricket::AudioCodec codec; + std::string pltype(iter->begin().memberName()); + talk_base::FromString(pltype, &codec.id); + Json::Value codec_info((*iter)[pltype]); + std::string codec_name(ReadString(codec_info, "codec")); + std::vector tokens; + talk_base::split(codec_name, '/', &tokens); + codec.name = tokens[1]; + codec.clockrate = ReadUInt(codec_info, "clockrate"); + content->AddCodec(codec); + } + + return true; +} + +bool ParseVideoCodec(const Json::Value& value, + cricket::VideoContentDescription* content) { + std::vector rtpmap(ReadValues(value, "rtpmap")); + if (rtpmap.empty()) + return false; + + std::vector::const_iterator iter = + rtpmap.begin(); + std::vector::const_iterator iter_end = + rtpmap.end(); + for (; iter != iter_end; ++iter) { + cricket::VideoCodec codec; + std::string pltype(iter->begin().memberName()); + talk_base::FromString(pltype, &codec.id); + Json::Value codec_info((*iter)[pltype]); + std::vector tokens; + talk_base::split(codec_info["codec"].asString(), '/', &tokens); + codec.name = tokens[1]; + content->AddCodec(codec); + } + return true; +} + +bool ParseICECandidates(const Json::Value& value, + std::vector* candidates) { + Json::Value attributes(ReadValue(value, "attributes")); + std::string ice_pwd(ReadString(attributes, "ice-pwd")); + std::string ice_ufrag(ReadString(attributes, "ice-ufrag")); + + std::vector jcandidates(ReadValues(attributes, "candidate")); + + std::vector::const_iterator iter = + jcandidates.begin(); + std::vector::const_iterator iter_end = + jcandidates.end(); + char buffer[16]; + for (; iter != iter_end; ++iter) { + cricket::Candidate cand; + std::string str; + str = ReadUInt(*iter, "generation"); + cand.set_generation_str(str); + str = ReadString(*iter, "proto"); + cand.set_protocol(str); + double priority = ReadDouble(*iter, "priority"); + talk_base::sprintfn(buffer, ARRAY_SIZE(buffer), "%f", priority); + cand.set_preference_str(buffer); + talk_base::SocketAddress addr; + str = ReadString(*iter, "ip"); + addr.SetIP(str); + str = ReadString(*iter, "port"); + int port; + talk_base::FromString(str, &port); + addr.SetPort(port); + cand.set_address(addr); + str = ReadString(*iter, "type"); + cand.set_type(str); + str = ReadString(*iter, "name"); + cand.set_name(str); + str = ReadString(*iter, "network_name"); + cand.set_network_name(str); + str = ReadString(*iter, "username"); + cand.set_username(str); + str = ReadString(*iter, "password"); + cand.set_password(str); + candidates->push_back(cand); + } + return true; +} + +std::vector ReadValues( + const Json::Value& value, const std::string& key) { + std::vector objects; + for (size_t i = 0; i < value[key].size(); ++i) { + objects.push_back(value[key][i]); + } + return objects; +} + +Json::Value ReadValue(const Json::Value& value, const std::string& key) { + return value[key]; +} + +std::string ReadString(const Json::Value& value, const std::string& key) { + return value[key].asString(); +} + +uint32 ReadUInt(const Json::Value& value, const std::string& key) { + return value[key].asUInt(); +} + +double ReadDouble(const Json::Value& value, const std::string& key) { + return value[key].asDouble(); +} + +// Add values +void Append(Json::Value* object, const std::string& key, bool value) { + (*object)[key] = Json::Value(value); +} + +void Append(Json::Value* object, const std::string& key, char * value) { + (*object)[key] = Json::Value(value); +} +void Append(Json::Value* object, const std::string& key, double value) { + (*object)[key] = Json::Value(value); +} +void Append(Json::Value* object, const std::string& key, float value) { + (*object)[key] = Json::Value(value); +} +void Append(Json::Value* object, const std::string& key, int value) { + (*object)[key] = Json::Value(value); +} +void Append(Json::Value* object, const std::string& key, + const std::string& value) { + (*object)[key] = Json::Value(value); +} +void Append(Json::Value* object, const std::string& key, uint32 value) { + (*object)[key] = Json::Value(value); +} + +void Append(Json::Value* object, const std::string& key, + const Json::Value& value) { + (*object)[key] = value; +} + +void Append(Json::Value* object, + const std::string & key, + const std::vector& values) { + for (std::vector::const_iterator iter = values.begin(); + iter != values.end(); ++iter) { + (*object)[key].append(*iter); + } +} + +} // namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/webrtc_json.h b/third_party_mods/libjingle/source/talk/app/webrtc/webrtc_json.h similarity index 59% rename from third_party_mods/libjingle/source/talk/app/webrtc_json.h rename to third_party_mods/libjingle/source/talk/app/webrtc/webrtc_json.h index 7ac57a644..81652704a 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc_json.h +++ b/third_party_mods/libjingle/source/talk/app/webrtc/webrtc_json.h @@ -44,6 +44,7 @@ class VideoContentDescription; struct ContentInfo; class SessionDescription; } + struct StunServiceDetails { std::string host; std::string service; @@ -53,20 +54,21 @@ struct StunServiceDetails { namespace webrtc { bool GetConnectionMediator(const Json::Value& value, - std::string& connectionMediator); -bool GetStunServer(const Json::Value& value, StunServiceDetails& stun); -bool GetTurnServer(const Json::Value& value, std::string& turnServer); + std::string* connection_mediator); +bool GetStunServer(const Json::Value& value, StunServiceDetails* stun); +bool GetTurnServer(const Json::Value& value, std::string* turn_server); bool FromJsonToAVCodec(const Json::Value& value, cricket::AudioContentDescription* audio, cricket::VideoContentDescription* video); -std::vector ReadValues(Json::Value& value, const std::string& key); +std::vector ReadValues(const Json::Value& value, + const std::string& key); bool BuildMediaMessage( - const cricket::ContentInfo* content_info, + const cricket::ContentInfo& content_info, const std::vector& candidates, bool video, - Json::Value& value); + Json::Value* value); bool GetJSONSignalingMessage( const cricket::SessionDescription* sdp, @@ -74,43 +76,46 @@ bool GetJSONSignalingMessage( std::string* signaling_message); bool BuildRtpMapParams( - const cricket::ContentInfo* audio_offer, + const cricket::ContentInfo& audio_offer, bool video, - std::vector& rtpmap); + std::vector* rtpmap); bool BuildAttributes(const std::vector& candidates, bool video, - std::vector& jcandidates); + std::vector* jcandidates); std::string Serialize(const Json::Value& value); bool Deserialize(const std::string& message, Json::Value& value); bool ParseJSONSignalingMessage(const std::string& signaling_message, cricket::SessionDescription*& sdp, - std::vector& candidates); -bool ParseAudioCodec(Json::Value value, cricket::AudioContentDescription* content); -bool ParseVideoCodec(Json::Value value, cricket::VideoContentDescription* content); -bool ParseICECandidates(Json::Value& value, - std::vector& candidates); -Json::Value ReadValue(Json::Value& value, const std::string& key); -std::string ReadString(Json::Value& value, const std::string& key); -double ReadDouble(Json::Value& value, const std::string& key); -uint32 ReadUInt(Json::Value& value, const std::string& key); + std::vector* candidates); +bool ParseAudioCodec(const Json::Value& value, + cricket::AudioContentDescription* content); +bool ParseVideoCodec(const Json::Value& value, + cricket::VideoContentDescription* content); +bool ParseICECandidates(const Json::Value& value, + std::vector* candidates); +Json::Value ReadValue(const Json::Value& value, const std::string& key); +std::string ReadString(const Json::Value& value, const std::string& key); +double ReadDouble(const Json::Value& value, const std::string& key); +uint32 ReadUInt(const Json::Value& value, const std::string& key); // Add values -void Append(Json::Value& object, const std::string& key, bool value); +void Append(Json::Value* object, const std::string& key, bool value); -void Append(Json::Value& object, const std::string& key, char * value); -void Append(Json::Value& object, const std::string& key, double value); -void Append(Json::Value& object, const std::string& key, float value); -void Append(Json::Value& object, const std::string& key, int value); -void Append(Json::Value& object, const std::string& key, std::string value); -void Append(Json::Value& object, const std::string& key, uint32 value); -void Append(Json::Value& object, const std::string& key, Json::Value value); -void Append(Json::Value & object, - const std::string & key, - std::vector& values); +void Append(Json::Value* object, const std::string& key, char * value); +void Append(Json::Value* object, const std::string& key, double value); +void Append(Json::Value* object, const std::string& key, float value); +void Append(Json::Value* object, const std::string& key, int value); +void Append(Json::Value* object, const std::string& key, + const std::string& value); +void Append(Json::Value* object, const std::string& key, uint32 value); +void Append(Json::Value* object, const std::string& key, + const Json::Value& value); +void Append(Json::Value* object, + const std::string& key, + const std::vector& values); } - -#endif // TALK_APP_WEBRTC_WEBRTC_JSON_H_ +#endif // TALK_APP_WEBRTC_WEBRTC_JSON_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/webrtcsession.cc b/third_party_mods/libjingle/source/talk/app/webrtc/webrtcsession.cc new file mode 100644 index 000000000..1ed16ea0a --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/webrtcsession.cc @@ -0,0 +1,693 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/app/webrtc/webrtcsession.h" + +#include +#include + +#include "talk/base/common.h" +#include "talk/base/json.h" +#include "talk/base/scoped_ptr.h" +#include "talk/p2p/base/constants.h" +#include "talk/p2p/base/sessiondescription.h" +#include "talk/p2p/base/p2ptransport.h" +#include "talk/session/phone/channel.h" +#include "talk/session/phone/channelmanager.h" +#include "talk/session/phone/mediasessionclient.h" +#include "talk/session/phone/voicechannel.h" + +namespace webrtc { + +enum { + MSG_CANDIDATE_TIMEOUT = 101, + MSG_WEBRTC_CREATE_TRANSPORT, + MSG_WEBRTC_DELETE_TRANSPORT, +}; + +static const int kAudioMonitorPollFrequency = 100; +static const int kMonitorPollFrequency = 1000; + +// We allow 30 seconds to establish a connection; beyond that we consider +// it an error +static const int kCallSetupTimeout = 30 * 1000; +// A loss of connectivity is probably due to the Internet connection going +// down, and it might take a while to come back on wireless networks, so we +// use a longer timeout for that. +static const int kCallLostTimeout = 60 * 1000; + +typedef std::vector StreamMap; // not really a map (vector) +static const char kVideoStream[] = "video_rtp"; +static const char kAudioStream[] = "rtp"; + +const char WebRTCSession::kOutgoingDirection[] = "s"; +const char WebRTCSession::kIncomingDirection[] = "r"; + +WebRTCSession::WebRTCSession( + const std::string& id, + const std::string& direction, + cricket::PortAllocator* allocator, + cricket::ChannelManager* channelmgr, + talk_base::Thread* signaling_thread) + : BaseSession(signaling_thread), + transport_(NULL), + channel_manager_(channelmgr), + all_transports_writable_(false), + muted_(false), + camera_muted_(false), + setup_timeout_(kCallSetupTimeout), + signaling_thread_(signaling_thread), + id_(id), + incoming_(direction == kIncomingDirection), + port_allocator_(allocator) { + BaseSession::sid_ = id; +} + +WebRTCSession::~WebRTCSession() { + RemoveAllStreams(); + if (state_ != STATE_RECEIVEDTERMINATE) { + Terminate(); + } + signaling_thread_->Send(this, MSG_WEBRTC_DELETE_TRANSPORT, NULL); +} + +bool WebRTCSession::Initiate() { + signaling_thread_->Send(this, MSG_WEBRTC_CREATE_TRANSPORT, NULL); + if (transport_ == NULL) { + return false; + } + transport_->set_allow_local_ips(true); + + // start transports + transport_->SignalRequestSignaling.connect( + this, &WebRTCSession::OnRequestSignaling); + transport_->SignalCandidatesReady.connect( + this, &WebRTCSession::OnCandidatesReady); + transport_->SignalWritableState.connect( + this, &WebRTCSession::OnWritableState); + // Limit the amount of time that setting up a call may take. + StartTransportTimeout(kCallSetupTimeout); + return true; +} + +cricket::Transport* WebRTCSession::CreateTransport() { + ASSERT(signaling_thread()->IsCurrent()); + return new cricket::P2PTransport( + talk_base::Thread::Current(), + channel_manager_->worker_thread(), port_allocator()); +} + +bool WebRTCSession::CreateVoiceChannel(const std::string& stream_id) { + StreamInfo* stream_info = new StreamInfo(stream_id); + stream_info->video = false; + streams_.push_back(stream_info); + + // RTCP disabled + cricket::VoiceChannel* voice_channel = + channel_manager_->CreateVoiceChannel(this, stream_id, false); + ASSERT(voice_channel != NULL); + stream_info->channel = voice_channel; + + if (incoming()) { + SignalAddStream(stream_id, false); + } else { + SignalRtcMediaChannelCreated(stream_id, false); + } + return true; +} + +bool WebRTCSession::CreateVideoChannel(const std::string& stream_id) { + StreamInfo* stream_info = new StreamInfo(stream_id); + stream_info->video = true; + streams_.push_back(stream_info); + + // RTCP disabled + cricket::VideoChannel* video_channel = + channel_manager_->CreateVideoChannel(this, stream_id, false, NULL); + ASSERT(video_channel != NULL); + stream_info->channel = video_channel; + + if (incoming()) { + SignalAddStream(stream_id, true); + } else { + SignalRtcMediaChannelCreated(stream_id, true); + } + return true; +} + + +cricket::TransportChannel* WebRTCSession::CreateChannel( + const std::string& content_name, + const std::string& name) { + if (!transport_) { + return NULL; + } + std::string type; + if (content_name.compare(kVideoStream) == 0) { + type = cricket::NS_GINGLE_VIDEO; + } else { + type = cricket::NS_GINGLE_AUDIO; + } + cricket::TransportChannel* transport_channel = + transport_->CreateChannel(name, type); + ASSERT(transport_channel != NULL); + transport_channels_[name] = transport_channel; + + StreamMap::iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + StreamInfo* stream_info = (*iter); + if (stream_info->stream_id.compare(content_name) == 0) { + ASSERT(!stream_info->channel); + stream_info->transport = transport_channel; + break; + } + } + return transport_channel; +} + +cricket::TransportChannel* WebRTCSession::GetChannel( + const std::string& content_name, const std::string& name) { + if (!transport_) + return NULL; + + StreamMap::iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + if (content_name.compare((*iter)->stream_id) == 0) { + return (*iter)->transport; + } + } + return NULL; +} + +void WebRTCSession::DestroyChannel( + const std::string& content_name, const std::string& name) { + if (!transport_) + return; + + transport_->DestroyChannel(name); + + StreamMap::iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + if (content_name.compare((*iter)->stream_id) == 0) { + (*iter)->transport = NULL; + streams_.erase(iter); + break; + } + } +} + +void WebRTCSession::OnMessage(talk_base::Message* message) { + switch (message->message_id) { + case MSG_CANDIDATE_TIMEOUT: + if (transport_->writable()) { + // This should never happen: The timout triggered even + // though a call was successfully set up. + ASSERT(false); + } + SignalFailedCall(); + break; + case MSG_WEBRTC_CREATE_TRANSPORT: + transport_ = CreateTransport(); + break; + case MSG_WEBRTC_DELETE_TRANSPORT: + if (transport_) { + delete transport_; + transport_ = NULL; + } + break; + default: + cricket::BaseSession::OnMessage(message); + break; + } +} + +bool WebRTCSession::Connect() { + if (streams_.empty()) { + // nothing to initiate + return false; + } + // lets connect all the transport channels created before for this session + transport_->ConnectChannels(); + + // create an offer now. This is to call SetState + // Actual offer will be send when OnCandidatesReady callback received + cricket::SessionDescription* offer = CreateOffer(); + set_local_description(offer); + SetState((incoming()) ? STATE_SENTACCEPT : STATE_SENTINITIATE); + + // Enable all the channels + EnableAllStreams(); + SetVideoCapture(true); + return true; +} + +bool WebRTCSession::SetVideoRenderer(const std::string& stream_id, + cricket::VideoRenderer* renderer) { + bool ret = false; + StreamMap::iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + StreamInfo* stream_info = (*iter); + if (stream_info->stream_id.compare(stream_id) == 0) { + ASSERT(stream_info->channel != NULL); + ASSERT(stream_info->video); + cricket::VideoChannel* channel = static_cast( + stream_info->channel); + ret = channel->SetRenderer(0, renderer); + break; + } + } + return ret; +} + +bool WebRTCSession::SetVideoCapture(bool capture) { + channel_manager_->SetVideoCapture(capture); + return true; +} + +bool WebRTCSession::RemoveStream(const std::string& stream_id) { + bool ret = false; + StreamMap::iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + StreamInfo* sinfo = (*iter); + if (sinfo->stream_id.compare(stream_id) == 0) { + DisableLocalCandidate(sinfo->transport->name()); + if (!sinfo->video) { + cricket::VoiceChannel* channel = static_cast ( + sinfo->channel); + channel->Enable(false); + channel_manager_->DestroyVoiceChannel(channel); + } else { + cricket::VideoChannel* channel = static_cast ( + sinfo->channel); + channel->Enable(false); + channel_manager_->DestroyVideoChannel(channel); + } + // channel and transport will be deleted in + // DestroyVoiceChannel/DestroyVideoChannel + ret = true; + break; + } + } + if (!ret) { + LOG(LERROR) << "No streams found for stream id " << stream_id; + // TODO(ronghuawu): trigger onError callback + } + return ret; +} + +void WebRTCSession::DisableLocalCandidate(const std::string& name) { + for (size_t i = 0; i < local_candidates_.size(); ++i) { + if (local_candidates_[i].name().compare(name) == 0) { + talk_base::SocketAddress address(local_candidates_[i].address().ip(), 0); + local_candidates_[i].set_address(address); + } + } +} + +void WebRTCSession::EnableAllStreams() { + StreamMap::const_iterator i; + for (i = streams_.begin(); i != streams_.end(); ++i) { + cricket::BaseChannel* channel = (*i)->channel; + if (channel) + channel->Enable(true); + } +} + +void WebRTCSession::RemoveAllStreams() { + // signaling_thread_->Post(this, MSG_RTC_REMOVEALLSTREAMS); + // First build a list of streams to remove and then remove them. + // The reason we do this is that if we remove the streams inside the + // loop, a stream might get removed while we're enumerating and the iterator + // will become invalid (and we crash). + // streams_ entry will be removed from ChannelManager callback method + // DestroyChannel + std::vector streams_to_remove; + StreamMap::iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) + streams_to_remove.push_back((*iter)->stream_id); + + for (std::vector::iterator i = streams_to_remove.begin(); + i != streams_to_remove.end(); ++i) { + RemoveStream(*i); + } + + SignalRemoveStream(this); +} + +bool WebRTCSession::HasStream(const std::string& stream_id) const { + StreamMap::const_iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + StreamInfo* sinfo = (*iter); + if (stream_id.compare(sinfo->stream_id) == 0) { + return true; + } + } + return false; +} + +bool WebRTCSession::HasStream(bool video) const { + StreamMap::const_iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + StreamInfo* sinfo = (*iter); + if (sinfo->video == video) { + return true; + } + } + return false; +} + +bool WebRTCSession::HasAudioStream() const { + return HasStream(false); +} + +bool WebRTCSession::HasVideoStream() const { + return HasStream(true); +} + +talk_base::Thread* WebRTCSession::worker_thread() { + return channel_manager_->worker_thread(); +} + +void WebRTCSession::OnRequestSignaling(cricket::Transport* transport) { + transport->OnSignalingReady(); +} + +void WebRTCSession::OnWritableState(cricket::Transport* transport) { + ASSERT(transport == transport_); + const bool all_transports_writable = transport_->writable(); + if (all_transports_writable) { + if (all_transports_writable != all_transports_writable_) { + signaling_thread_->Clear(this, MSG_CANDIDATE_TIMEOUT); + } else { + // At one point all channels were writable and we had full connectivity, + // but then we lost it. Start the timeout again to kill the call if it + // doesn't come back. + StartTransportTimeout(kCallLostTimeout); + } + all_transports_writable_ = all_transports_writable; + } + NotifyTransportState(); + return; +} + + + +void WebRTCSession::StartTransportTimeout(int timeout) { + talk_base::Thread::Current()->PostDelayed(timeout, this, + MSG_CANDIDATE_TIMEOUT, + NULL); +} + +void WebRTCSession::NotifyTransportState() { +} + +bool WebRTCSession::OnInitiateMessage( + cricket::SessionDescription* offer, + const std::vector& candidates) { + if (!offer) { + LOG(LERROR) << "No SessionDescription from peer"; + return false; + } + + talk_base::scoped_ptr answer; + answer.reset(CreateAnswer(offer)); + + const cricket::ContentInfo* audio_content = GetFirstAudioContent( + answer.get()); + const cricket::ContentInfo* video_content = GetFirstVideoContent( + answer.get()); + + if (!audio_content && !video_content) { + return false; + } + + bool ret = true; + if (audio_content) { + ret = !HasAudioStream() && + CreateVoiceChannel(audio_content->name); + if (!ret) { + LOG(LERROR) << "Failed to create voice channel for " + << audio_content->name; + return false; + } + } + + if (video_content) { + ret = !HasVideoStream() && + CreateVideoChannel(video_content->name); + if (!ret) { + LOG(LERROR) << "Failed to create video channel for " + << video_content->name; + return false; + } + } + + set_remote_description(offer); + SetState(STATE_RECEIVEDINITIATE); + + transport_->ConnectChannels(); + EnableAllStreams(); + + set_local_description(answer.release()); + SetState(STATE_SENTACCEPT); + return true; +} + +bool WebRTCSession::OnRemoteDescription( + cricket::SessionDescription* desc, + const std::vector& candidates) { + if (state() == STATE_SENTACCEPT || + state() == STATE_RECEIVEDACCEPT || + state() == STATE_INPROGRESS) { + if (CheckForStreamDeleteMessage(candidates)) { + return OnRemoteDescriptionUpdate(desc, candidates); + } else { + transport_->OnRemoteCandidates(candidates); + return true; + } + } + + // Session description is always accepted. + set_remote_description(desc); + SetState(STATE_RECEIVEDACCEPT); + // Will trigger OnWritableState() if successfull. + transport_->OnRemoteCandidates(candidates); + return true; +} + +bool WebRTCSession::CheckForStreamDeleteMessage( + const std::vector& candidates) { + for (size_t i = 0; i < candidates.size(); ++i) { + if (candidates[i].address().port() == 0) { + return true; + } + } + return false; +} + +bool WebRTCSession::OnRemoteDescriptionUpdate( + const cricket::SessionDescription* desc, + const std::vector& candidates) { + // This will be called when session is in connected state + // In this state session expects signaling message for any removed + // streamed by the peer. + // check for candidates port, if its equal to 0, remove that stream + // and provide callback OnRemoveStream else keep as it is + + for (size_t i = 0; i < candidates.size(); ++i) { + if (candidates[i].address().port() == 0) { + RemoveStreamOnRequest(candidates[i]); + } + } + return true; +} + +void WebRTCSession::RemoveStreamOnRequest( + const cricket::Candidate& candidate) { + // 1. Get Transport corresponding to candidate name + // 2. Get StreamInfo for the transport found in step 1 + // 3. call ChannelManager Destroy voice/video method +// + TransportChannelMap::iterator iter = + transport_channels_.find(candidate.name()); + if (iter == transport_channels_.end()) { + return; + } + + cricket::TransportChannel* transport = iter->second; + std::vector::iterator siter; + for (siter = streams_.begin(); siter != streams_.end(); ++siter) { + StreamInfo* stream_info = (*siter); + if (stream_info->transport == transport) { + if (!stream_info->video) { + cricket::VoiceChannel* channel = static_cast ( + stream_info->channel); + channel->Enable(false); + channel_manager_->DestroyVoiceChannel(channel); + } else { + cricket::VideoChannel* channel = static_cast ( + stream_info->channel); + channel->Enable(false); + channel_manager_->DestroyVideoChannel(channel); + } + SignalRemoveStream2((*siter)->stream_id, (*siter)->video); + break; + } + } +} + +cricket::SessionDescription* WebRTCSession::CreateOffer() { + cricket::SessionDescription* offer = new cricket::SessionDescription(); + StreamMap::iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + if ((*iter)->video) { + // add video codecs, if there is video stream added + cricket::VideoContentDescription* video = + new cricket::VideoContentDescription(); + std::vector video_codecs; + channel_manager_->GetSupportedVideoCodecs(&video_codecs); + for (VideoCodecs::const_iterator codec = video_codecs.begin(); + codec != video_codecs.end(); ++codec) { + video->AddCodec(*codec); + } + + video->SortCodecs(); + offer->AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP, video); + } else { + cricket::AudioContentDescription* audio = + new cricket::AudioContentDescription(); + + std::vector audio_codecs; + channel_manager_->GetSupportedAudioCodecs(&audio_codecs); + for (AudioCodecs::const_iterator codec = audio_codecs.begin(); + codec != audio_codecs.end(); ++codec) { + audio->AddCodec(*codec); + } + + audio->SortCodecs(); + offer->AddContent(cricket::CN_AUDIO, cricket::NS_JINGLE_RTP, audio); + } + } + return offer; +} + +cricket::SessionDescription* WebRTCSession::CreateAnswer( + const cricket::SessionDescription* offer) { + cricket::SessionDescription* answer = new cricket::SessionDescription(); + + const cricket::ContentInfo* audio_content = GetFirstAudioContent(offer); + if (audio_content) { + const cricket::AudioContentDescription* audio_offer = + static_cast( + audio_content->description); + + cricket::AudioContentDescription* audio_accept = + new cricket::AudioContentDescription(); + AudioCodecs audio_codecs; + channel_manager_->GetSupportedAudioCodecs(&audio_codecs); + + for (AudioCodecs::const_iterator ours = audio_codecs.begin(); + ours != audio_codecs.end(); ++ours) { + for (AudioCodecs::const_iterator theirs = audio_offer->codecs().begin(); + theirs != audio_offer->codecs().end(); ++theirs) { + if (ours->Matches(*theirs)) { + cricket::AudioCodec negotiated(*ours); + negotiated.id = theirs->id; + audio_accept->AddCodec(negotiated); + } + } + } + audio_accept->SortCodecs(); + answer->AddContent(audio_content->name, audio_content->type, audio_accept); + } + + const cricket::ContentInfo* video_content = GetFirstVideoContent(offer); + if (video_content) { + const cricket::VideoContentDescription* video_offer = + static_cast( + video_content->description); + + cricket::VideoContentDescription* video_accept = + new cricket::VideoContentDescription(); + VideoCodecs video_codecs; + channel_manager_->GetSupportedVideoCodecs(&video_codecs); + + for (VideoCodecs::const_iterator ours = video_codecs.begin(); + ours != video_codecs.end(); ++ours) { + for (VideoCodecs::const_iterator theirs = video_offer->codecs().begin(); + theirs != video_offer->codecs().end(); ++theirs) { + if (ours->Matches(*theirs)) { + cricket::VideoCodec negotiated(*ours); + negotiated.id = theirs->id; + video_accept->AddCodec(negotiated); + } + } + } + video_accept->SortCodecs(); + answer->AddContent(video_content->name, video_content->type, video_accept); + } + return answer; +} + +void WebRTCSession::OnMute(bool mute) { + StreamMap::iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + if (!(*iter)->video) { + cricket::VoiceChannel* voice_channel = + static_cast((*iter)->channel); + ASSERT(voice_channel != NULL); + voice_channel->Mute(mute); + } + } +} + +void WebRTCSession::OnCameraMute(bool mute) { + StreamMap::iterator iter; + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { + if ((*iter)->video) { + cricket::VideoChannel* video_channel = + static_cast((*iter)->channel); + ASSERT(video_channel != NULL); + video_channel->Mute(mute); + } + } +} + +void WebRTCSession::SetError(Error error) { + BaseSession::SetError(error); +} + +void WebRTCSession::OnCandidatesReady( + cricket::Transport* transport, + const std::vector& candidates) { + std::vector::const_iterator iter; + for (iter = candidates.begin(); iter != candidates.end(); ++iter) { + local_candidates_.push_back(*iter); + } + SignalLocalDescription(local_description(), candidates); +} +} /* namespace webrtc */ diff --git a/third_party_mods/libjingle/source/talk/app/webrtcsessionimpl.h b/third_party_mods/libjingle/source/talk/app/webrtc/webrtcsession.h similarity index 54% rename from third_party_mods/libjingle/source/talk/app/webrtcsessionimpl.h rename to third_party_mods/libjingle/source/talk/app/webrtc/webrtcsession.h index 95db057c1..ef9afca22 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtcsessionimpl.h +++ b/third_party_mods/libjingle/source/talk/app/webrtc/webrtcsession.h @@ -25,18 +25,18 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_APP_WEBRTC_WEBRTCSESSIONIMPL_H_ -#define TALK_APP_WEBRTC_WEBRTCSESSIONIMPL_H_ +#ifndef TALK_APP_WEBRTC_WEBRTCSESSION_H_ +#define TALK_APP_WEBRTC_WEBRTCSESSION_H_ #include #include +#include "talk/base/logging.h" #include "talk/base/messagehandler.h" #include "talk/p2p/base/candidate.h" +#include "talk/p2p/base/session.h" #include "talk/session/phone/channel.h" #include "talk/session/phone/mediachannel.h" -#include "talk/app/pc_transport_impl.h" -#include "talk/app/webrtcsession.h" namespace cricket { class ChannelManager; @@ -54,62 +54,44 @@ class Value; namespace webrtc { struct StreamInfo { - StreamInfo(const std::string stream_id) + explicit StreamInfo(const std::string stream_id) : channel(NULL), transport(NULL), video(false), - stream_id(stream_id), - media_channel(-1) {} + stream_id(stream_id) {} StreamInfo() : channel(NULL), transport(NULL), - video(false), - media_channel(-1) {} + video(false) {} cricket::BaseChannel* channel; - PC_Transport_Impl* transport; //TODO - add RTCP transport channel + cricket::TransportChannel* transport; bool video; std::string stream_id; - int media_channel; }; typedef std::vector AudioCodecs; typedef std::vector VideoCodecs; -class ExternalRenderer; -class PeerConnection; - -class WebRTCSessionImpl: public WebRTCSession { - +class WebRTCSession : public cricket::BaseSession { public: - - WebRTCSessionImpl(const std::string& id, + WebRTCSession(const std::string& id, const std::string& direction, cricket::PortAllocator* allocator, cricket::ChannelManager* channelmgr, - PeerConnection* connection, talk_base::Thread* signaling_thread); - ~WebRTCSessionImpl(); - virtual bool Initiate(); - virtual bool OnRemoteDescription(Json::Value& desc); - virtual bool OnRemoteDescription(const cricket::SessionDescription* sdp, - std::vector& candidates); - virtual bool OnInitiateMessage(const cricket::SessionDescription* sdp, - std::vector& candidates); - virtual void OnMute(bool mute); - virtual void OnCameraMute(bool mute); - - // Override from BaseSession to allow setting errors from other threads - // than the signaling thread. - virtual void SetError(Error error); - - bool muted() const { return muted_; } - bool camera_muted() const { return camera_muted_; } - - bool CreateP2PTransportChannel(const std::string& stream_id, bool video); + ~WebRTCSession(); + bool Initiate(); + bool Connect(); + bool OnRemoteDescription(cricket::SessionDescription* sdp, + const std::vector& candidates); + bool OnInitiateMessage(cricket::SessionDescription* sdp, + const std::vector& candidates); + void OnMute(bool mute); + void OnCameraMute(bool mute); bool CreateVoiceChannel(const std::string& stream_id); bool CreateVideoChannel(const std::string& stream_id); bool RemoveStream(const std::string& stream_id); @@ -125,45 +107,32 @@ class WebRTCSessionImpl: public WebRTCSession { // Returns true if there's one or more video channels in the session. bool HasVideoStream() const; - void OnCandidateReady(const cricket::Candidate& candidate); - void OnStateChange(P2PTransportClass::State state, - cricket::TransportChannel* channel); - void OnMessageReceived(const char* data, size_t data_size); bool SetVideoRenderer(const std::string& stream_id, cricket::VideoRenderer* renderer); - bool SetVideoRenderer(const std::string& stream_id, - ExternalRenderer* external_renderer); - sigslot::signal2 SignalVideoChannel; - sigslot::signal2 SignalVoiceChannel; - sigslot::signal1 SignalOnRemoveStream; + sigslot::signal1 SignalRemoveStream; + sigslot::signal2 SignalAddStream; + sigslot::signal2 SignalRemoveStream2; + sigslot::signal2 SignalRtcMediaChannelCreated; + // Triggered when the local candidate is ready + sigslot::signal2&> SignalLocalDescription; + // This callback will trigger if setting up a call times out. + sigslot::signal0<> SignalFailedCall; - void OnVoiceChannelCreated(cricket::VoiceChannel* voice_channel, - std::string& stream_id); - void OnVideoChannelCreated(cricket::VideoChannel* video_channel, - std::string& stream_id); - - void ChannelEnable(cricket::BaseChannel* channel, bool enable); - - std::vector& local_candidates() { + bool muted() const { return muted_; } + bool camera_muted() const { return camera_muted_; } + const std::vector& local_candidates() { return local_candidates_; } + const std::string& id() const { return id_; } + bool incoming() const { return incoming_; } + cricket::PortAllocator* port_allocator() const { return port_allocator_; } + talk_base::Thread* signaling_thread() const { return signaling_thread_; } - private: - void ChannelEnable_w(cricket::BaseChannel* channel, bool enable); - - void OnVoiceChannelError(cricket::VoiceChannel* voice_channel, uint32 ssrc, - cricket::VoiceMediaChannel::Error error); - void OnVideoChannelError(cricket::VideoChannel* video_channel, uint32 ssrc, - cricket::VideoMediaChannel::Error error); - - // methods signaled by the transport - void OnRequestSignaling(cricket::Transport* transport); - void OnCandidatesReady(cricket::Transport* transport, - const std::vector& candidates); - void OnWritableState(cricket::Transport* transport); - - // transport-management overrides from cricket::BaseSession + protected: + // methods from cricket::BaseSession + virtual void SetError(cricket::BaseSession::Error error); virtual cricket::TransportChannel* CreateChannel( const std::string& content_name, const std::string& name); virtual cricket::TransportChannel* GetChannel( @@ -171,68 +140,74 @@ class WebRTCSessionImpl: public WebRTCSession { virtual void DestroyChannel( const std::string& content_name, const std::string& name); - virtual talk_base::Thread* worker_thread() { - return NULL; + private: + // Dummy functions inherited from cricket::BaseSession. + // They should never be called. + virtual bool Accept(const cricket::SessionDescription* sdesc) { + return true; } - void SendLocalDescription(); + virtual bool Reject(const std::string& reason) { + return true; + } + virtual bool TerminateWithReason(const std::string& reason) { + return true; + } + virtual talk_base::Thread* worker_thread(); + + // methods signaled by the transport + void OnRequestSignaling(cricket::Transport* transport); + void OnCandidatesReady(cricket::Transport* transport, + const std::vector& candidates); + void OnWritableState(cricket::Transport* transport); + void OnTransportError(cricket::Transport* transport); + void OnChannelGone(cricket::Transport* transport); + + bool CheckForStreamDeleteMessage( + const std::vector& candidates); void UpdateTransportWritableState(); bool CheckAllTransportsWritable(); void StartTransportTimeout(int timeout); - void ClearTransportTimeout(); void NotifyTransportState(); cricket::SessionDescription* CreateOffer(); cricket::SessionDescription* CreateAnswer( const cricket::SessionDescription* answer); - //from MessageHandler + // from MessageHandler virtual void OnMessage(talk_base::Message* message); - private: - typedef std::map TransportChannelMap; + virtual cricket::Transport* CreateTransport(); + cricket::Transport* GetTransport(); - cricket::VideoChannel* CreateVideoChannel_w( - const std::string& content_name, - bool rtcp, - cricket::VoiceChannel* voice_channel); + typedef std::map TransportChannelMap; - cricket::VoiceChannel* CreateVoiceChannel_w( - const std::string& content_name, - bool rtcp); - - void DestroyVoiceChannel_w(cricket::VoiceChannel* channel); - void DestroyVideoChannel_w(cricket::VideoChannel* channel); - void SignalOnWritableState_w(cricket::TransportChannel* channel); - - void SetSessionState(State state); - void SetSessionState_w(); bool SetVideoCapture(bool capture); - cricket::CaptureResult SetVideoCapture_w(bool capture); void DisableLocalCandidate(const std::string& name); bool OnRemoteDescriptionUpdate(const cricket::SessionDescription* desc, - std::vector& candidates); + const std::vector& candidates); void RemoveStreamOnRequest(const cricket::Candidate& candidate); - void RemoveStream_w(const std::string& stream_id); - void RemoveAllStreams_w(); - - void EnableAllStreams_w(); - - void SendLocalDescription_w(); + void EnableAllStreams(); + cricket::Transport* transport_; cricket::ChannelManager* channel_manager_; std::vector streams_; TransportChannelMap transport_channels_; - bool all_writable_; + bool all_transports_writable_; bool muted_; bool camera_muted_; int setup_timeout_; std::vector local_candidates_; - std::vector remote_candidates_; - State session_state_; - bool signal_initiated_; + + talk_base::Thread* signaling_thread_; + std::string id_; + bool incoming_; + cricket::PortAllocator* port_allocator_; + + static const char kIncomingDirection[]; + static const char kOutgoingDirection[]; }; -} /* namespace webrtc */ +} // namespace webrtc -#endif /* TALK_APP_WEBRTC_WEBRTCSESSIONIMPL_H_ */ +#endif // TALK_APP_WEBRTC_WEBRTCSESSION_H_ diff --git a/third_party_mods/libjingle/source/talk/app/webrtc/webrtcsession_unittest.cc b/third_party_mods/libjingle/source/talk/app/webrtc/webrtcsession_unittest.cc new file mode 100644 index 000000000..6a9eeaac4 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/app/webrtc/webrtcsession_unittest.cc @@ -0,0 +1,885 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "base/gunit.h" +#include "base/helpers.h" +#include "talk/app/webrtc/testing/timing.h" +#include "talk/app/webrtc/webrtcsession.h" +#include "talk/base/fakenetwork.h" +#include "talk/base/scoped_ptr.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/portallocator.h" +#include "talk/p2p/base/sessiondescription.h" +#include "talk/p2p/client/fakeportallocator.h" +#include "talk/session/phone/fakesession.h" +#include "talk/session/phone/mediasessionclient.h" + +cricket::VideoContentDescription* CopyVideoContentDescription( + const cricket::VideoContentDescription* video_description) { + cricket::VideoContentDescription* new_video_description = + new cricket::VideoContentDescription(); + cricket::VideoCodecs::const_iterator iter = + video_description->codecs().begin(); + for (; iter != video_description->codecs().end(); iter++) { + new_video_description->AddCodec(*iter); + } + new_video_description->SortCodecs(); + return new_video_description; +} + +cricket::AudioContentDescription* CopyAudioContentDescription( + const cricket::AudioContentDescription* audio_description) { + cricket::AudioContentDescription* new_audio_description = + new cricket::AudioContentDescription(); + cricket::AudioCodecs::const_iterator iter = + audio_description->codecs().begin(); + for (; iter != audio_description->codecs().end(); iter++) { + new_audio_description->AddCodec(*iter); + } + new_audio_description->SortCodecs(); + return new_audio_description; +} + +const cricket::ContentDescription* CopyContentDescription( + const cricket::ContentDescription* original) { + const cricket::MediaContentDescription* media = + static_cast(original); + const cricket::ContentDescription* new_content_description = NULL; + if (media->type() == cricket::MEDIA_TYPE_VIDEO) { + const cricket::VideoContentDescription* video_description = + static_cast(original); + new_content_description = static_cast + (CopyVideoContentDescription(video_description)); + } else if (media->type() == cricket::MEDIA_TYPE_AUDIO) { + const cricket::AudioContentDescription* audio_description = + static_cast(original); + new_content_description = static_cast + (CopyAudioContentDescription(audio_description)); + } else { + return NULL; + } + return new_content_description; +} + +cricket::ContentInfos CopyContentInfos(const cricket::ContentInfos& original) { + cricket::ContentInfos new_content_infos; + for (cricket::ContentInfos::const_iterator iter = original.begin(); + iter != original.end(); iter++) { + cricket::ContentInfo info; + info.name = (*iter).name; + info.type = (*iter).type; + info.description = CopyContentDescription((*iter).description); + } + return new_content_infos; +} + +cricket::SessionDescription* CopySessionDescription( + const cricket::SessionDescription* original) { + const cricket::ContentInfos& content_infos = original->contents(); + cricket::ContentInfos new_content_infos = CopyContentInfos(content_infos); + return new cricket::SessionDescription(new_content_infos); +} + +bool GenerateFakeSessionDescription(bool video, + cricket::SessionDescription** incoming_sdp) { + *incoming_sdp = new cricket::SessionDescription(); + if (*incoming_sdp == NULL) + return false; + const std::string name = video ? std::string(cricket::CN_VIDEO) : + std::string(cricket::CN_AUDIO); + cricket::ContentDescription* description = NULL; + if (video) { + cricket::VideoContentDescription* video_dsc = + new cricket::VideoContentDescription; + video_dsc->SortCodecs(); + description = static_cast(video_dsc); + } else { + cricket::AudioContentDescription* audio_dsc = + new cricket::AudioContentDescription(); + audio_dsc->SortCodecs(); + description = static_cast(audio_dsc); + } + + // Cannot fail. + (*incoming_sdp)->AddContent(name, cricket::NS_JINGLE_RTP, description); + return true; +} + +void GenerateFakeCandidate(bool video, + std::vector* candidates) { + // Next add a candidate. + // int port_index = 0; + std::string port_index_as_string("0"); + + cricket::Candidate candidate; + candidate.set_name("rtp"); + candidate.set_protocol("udp"); + talk_base::SocketAddress address("127.0.0.1", 1234); + candidate.set_address(address); + candidate.set_preference(1); + candidate.set_username("username" + port_index_as_string); + candidate.set_password(port_index_as_string); + candidate.set_type("local"); + candidate.set_network_name("network"); + candidate.set_generation(0); + + candidates->push_back(candidate); +} + + +bool GenerateFakeSession(bool video, cricket::SessionDescription** incoming_sdp, + std::vector* candidates) { + if (!GenerateFakeSessionDescription(video, incoming_sdp)) { + return false; + } + + GenerateFakeCandidate(video, candidates); + return true; +} + +class OnSignalImpl + : public sigslot::has_slots<> { + public: + enum CallbackId { + kNone, + kOnAddStream, + kOnRemoveStream2, + kOnRtcMediaChannelCreated, + kOnLocalDescription, + kOnFailedCall, + }; + OnSignalImpl() + : callback_ids_(), + last_stream_id_(""), + last_was_video_(false), + last_description_ptr_(NULL), + last_candidates_() { + } + virtual ~OnSignalImpl() { + delete last_description_ptr_; + last_description_ptr_ = NULL; + } + + void OnAddStream(const std::string& stream_id, bool video) { + callback_ids_.push_back(kOnAddStream); + last_stream_id_ = stream_id; + last_was_video_ = video; + } + void OnRemoveStream2(const std::string& stream_id, bool video) { + callback_ids_.push_back(kOnRemoveStream2); + last_stream_id_ = stream_id; + last_was_video_ = video; + } + void OnRtcMediaChannelCreated(const std::string& stream_id, + bool video) { + callback_ids_.push_back(kOnRtcMediaChannelCreated); + last_stream_id_ = stream_id; + last_was_video_ = video; + } + void OnLocalDescription( + const cricket::SessionDescription* desc, + const std::vector& candidates) { + callback_ids_.push_back(kOnLocalDescription); + delete last_description_ptr_; + last_description_ptr_ = CopySessionDescription(desc); + last_candidates_.clear(); + last_candidates_.insert(last_candidates_.end(), candidates.begin(), + candidates.end()); + } + cricket::SessionDescription* GetLocalDescription( + std::vector* candidates) { + if (last_candidates_.empty()) { + return NULL; + } + if (last_description_ptr_ == NULL) { + return NULL; + } + candidates->insert(candidates->end(), last_candidates_.begin(), + last_candidates_.end()); + return CopySessionDescription(last_description_ptr_); + } + + void OnFailedCall() { + callback_ids_.push_back(kOnFailedCall); + } + + CallbackId PopOldestCallback() { + if (callback_ids_.empty()) { + return kNone; + } + const CallbackId return_value = callback_ids_.front(); + callback_ids_.pop_front(); + return return_value; + } + + CallbackId PeekOldestCallback() { + if (callback_ids_.empty()) { + return kNone; + } + const CallbackId return_value = callback_ids_.front(); + return return_value; + } + + void Reset() { + callback_ids_.clear(); + last_stream_id_ = ""; + last_was_video_ = false; + delete last_description_ptr_; + last_description_ptr_ = NULL; + last_candidates_.clear(); + } + + protected: + std::list callback_ids_; + + std::string last_stream_id_; + bool last_was_video_; + cricket::SessionDescription* last_description_ptr_; + std::vector last_candidates_; +}; + +template +struct ReturnValue : public talk_base::MessageData { + ReturnValue() : return_value_() {} + T return_value_; +}; + +typedef ReturnValue ReturnBool; +typedef ReturnValue*> +ReturnCandidates; + +template +class PassArgument : public talk_base::MessageData { + public: + explicit PassArgument(const T& argument) : argument_(argument) {} + const T& argument() { return argument_; } + + protected: + T argument_; +}; + +typedef PassArgument PassBool; +typedef PassArgument PassError; +typedef PassArgument > + PassVoiceChannelString; +typedef PassArgument > + PassVideoChannelString; + +template +class ReturnBoolPassArgument : public talk_base::MessageData { + public: + explicit ReturnBoolPassArgument(const T& argument) + : argument_(argument) { return_value_ = false; } + const T& argument() { return argument_; } + bool return_value_; + + protected: + T argument_; +}; + +typedef ReturnBoolPassArgument > + ReturnBoolPassStringBool; +typedef ReturnBoolPassArgument ReturnBoolPassString; +typedef ReturnBoolPassArgument ReturnBoolPassBool; +typedef ReturnBoolPassArgument< + std::pair > + ReturnBoolPassStringVideoRenderer; + +class WebRTCSessionExtendedForTest : public webrtc::WebRTCSession { + public: + WebRTCSessionExtendedForTest(const std::string& id, + const std::string& direction, + cricket::PortAllocator* allocator, + cricket::ChannelManager* channelmgr, + talk_base::Thread* signaling_thread) + : WebRTCSession(id, direction, allocator, channelmgr, signaling_thread), + worker_thread_(channelmgr->worker_thread()) { + } + private: + virtual cricket::Transport* CreateTransport() { + ASSERT(signaling_thread()->IsCurrent()); + return static_cast (new cricket::FakeTransport( + signaling_thread(), + worker_thread_)); + } + talk_base::Thread* worker_thread_; +}; + +class WebRTCSessionTest : public OnSignalImpl, + public talk_base::MessageHandler { + public: + enum FunctionCallId { + kCallInitiate, + kCallConnect, + kCallOnRemoteDescription, + kCallOnInitiateMessage, + kCallOnMute, + kCallOnCameraMute, + kCallMuted, + kCallCameraMuted, + kCallCreateVoiceChannel, + kCallCreateVideoChannel, + kCallRemoveStream, + kCallRemoveAllStreams, + kCallHasStreamString, + kCallHasStreamBool, + kCallHasAudioStream, + kCallHasVideoStream, + kCallSetVideoRenderer, + kCallLocalCandidates + }; + enum {kInit = kCallLocalCandidates + 1}; + enum {kTerminate = kInit + 1}; + + static WebRTCSessionTest* CreateWebRTCSessionTest(bool receiving) { + WebRTCSessionTest* return_value = + new WebRTCSessionTest(); + if (return_value == NULL) { + return NULL; + } + if (!return_value->Init(receiving)) { + delete return_value; + return NULL; + } + return return_value; + } + + std::string DirectionAsString() { + // Direction is either "r"=incoming or "s"=outgoing. + return (receiving_) ? "r" : "s"; + } + + bool WaitForCallback(CallbackId id, int timeout_ms) { + bool success = false; + Timing my_timer; + for (int ms = 0; ms < timeout_ms; ms++) { + const CallbackId peek_id = PeekOldestCallback(); + if (peek_id == id) { + PopOldestCallback(); + success = true; + break; + } else if (peek_id != kNone) { + success = false; + break; + } + my_timer.IdleWait(0.001); + } + return success; + } + + bool Init(bool receiving) { + if (signaling_thread_ != NULL) + return false; + signaling_thread_ = new talk_base::Thread(); + + if (!signaling_thread_->SetName("signaling_thread test", this)) { + return false; + } + if (!signaling_thread_->Start()) { + return false; + } + receiving_ = receiving; + + ReturnBool return_value; + signaling_thread_->Send(this, kInit, &return_value); + return return_value.return_value_; + } + + void Init_s(talk_base::Message* message) { + ReturnBool* return_value = reinterpret_cast(message->pdata); + return_value->return_value_ = false; + + ASSERT_TRUE(worker_thread_ == NULL); + worker_thread_ = new talk_base::Thread(); + + if (!worker_thread_->SetName("worker thread test", this)) + return; + + if (!worker_thread_->Start()) + return; + + cricket::FakePortAllocator* fake_port_allocator = + new cricket::FakePortAllocator(worker_thread_, NULL); + fake_port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_STUN | + cricket::PORTALLOCATOR_DISABLE_RELAY | + cricket::PORTALLOCATOR_DISABLE_TCP); + + allocator_ = static_cast(fake_port_allocator); + + channel_manager_ = new cricket::ChannelManager(worker_thread_); + if (!channel_manager_->Init()) + return; + + talk_base::CreateRandomString(8, &id_); + + session_ = new webrtc::WebRTCSession( + id_, DirectionAsString() , allocator_, + channel_manager_, + signaling_thread_); + session_->SignalAddStream.connect( + static_cast (this), + &OnSignalImpl::OnAddStream); + session_->SignalRemoveStream2.connect( + static_cast (this), + &OnSignalImpl::OnRemoveStream2); + session_->SignalRtcMediaChannelCreated.connect( + static_cast (this), + &OnSignalImpl::OnRtcMediaChannelCreated); + session_->SignalLocalDescription.connect( + static_cast (this), + &OnSignalImpl::OnLocalDescription); + session_->SignalFailedCall.connect( + static_cast (this), + &OnSignalImpl::OnFailedCall); + + return_value->return_value_ = true; + return; + } + + void Terminate_s() { + delete session_; + delete channel_manager_; + delete allocator_; + } + + ~WebRTCSessionTest() { + if (signaling_thread_ != NULL) { + signaling_thread_->Send(this, kTerminate, NULL); + signaling_thread_->Stop(); + signaling_thread_->Clear(NULL); + delete signaling_thread_; + } + if (worker_thread_ != NULL) { + worker_thread_->Stop(); + worker_thread_->Clear(NULL); + delete worker_thread_; + } + } + + // All session APIs must be called from the signaling thread. + bool CallInitiate() { + ReturnBool return_value; + signaling_thread_->Send(this, kCallInitiate, &return_value); + return return_value.return_value_; + } + + bool CallConnect() { + ReturnBool return_value; + signaling_thread_->Send(this, kCallConnect, &return_value); + // This callback does not happen with FakeTransport! + if (!WaitForCallback(kOnLocalDescription, 1000)) { + return false; + } + return return_value.return_value_; + } + + bool CallOnRemoteDescription() { + ReturnBool return_value; + signaling_thread_->Send(this, kCallOnRemoteDescription, &return_value); + return return_value.return_value_; + } + + bool CallOnInitiateMessage() { + ReturnBool return_value; + signaling_thread_->Send(this, kCallOnInitiateMessage, &return_value); + return return_value.return_value_; + } + + void CallOnMute(bool mute) { + PassBool return_value(mute); + signaling_thread_->Send(this, kCallOnMute, &return_value); + } + + void CallOnCameraMute(bool mute) { + PassBool return_value(mute); + signaling_thread_->Send(this, kCallOnCameraMute, &return_value); + } + + bool CallMuted() { + ReturnBool return_value; + signaling_thread_->Send(this, kCallMuted, &return_value); + return return_value.return_value_; + } + + bool CallCameraMuted() { + ReturnBool return_value; + signaling_thread_->Send(this, kCallCameraMuted, &return_value); + return return_value.return_value_; + } + + bool CallCreateVoiceChannel(const std::string& stream_id) { + ReturnBoolPassString return_value(stream_id); + signaling_thread_->Send(this, kCallCreateVoiceChannel, &return_value); + if (!WaitForCallback(kOnRtcMediaChannelCreated, 1000)) { + return false; + } + return return_value.return_value_; + } + + bool CallCreateVideoChannel(const std::string& stream_id) { + ReturnBoolPassString return_value(stream_id); + signaling_thread_->Send(this, kCallCreateVideoChannel, &return_value); + return return_value.return_value_; + } + + bool CallRemoveStream(const std::string& stream_id) { + ReturnBoolPassString return_value(stream_id); + signaling_thread_->Send(this, kCallRemoveStream, &return_value); + return return_value.return_value_; + } + + void CallRemoveAllStreams() { + signaling_thread_->Send(this, kCallRemoveAllStreams, NULL); + } + + bool CallHasStream(const std::string& label) { + ReturnBoolPassString return_value(label); + signaling_thread_->Send(this, kCallHasStreamString, &return_value); + return return_value.return_value_; + } + + bool CallHasStream(bool video) { + ReturnBoolPassBool return_value(video); + signaling_thread_->Send(this, kCallHasStreamBool, &return_value); + return return_value.return_value_; + } + + bool CallHasAudioStream() { + ReturnBool return_value; + signaling_thread_->Send(this, kCallHasAudioStream, &return_value); + return return_value.return_value_; + } + + bool CallHasVideoStream() { + ReturnBool return_value; + signaling_thread_->Send(this, kCallHasVideoStream, &return_value); + return return_value.return_value_; + } + + bool CallSetVideoRenderer(const std::string& stream_id, + cricket::VideoRenderer* renderer) { + ReturnBoolPassStringVideoRenderer return_value(std::make_pair( + stream_id, renderer)); + signaling_thread_->Send(this, kCallSetVideoRenderer, &return_value); + return return_value.return_value_; + } + + const std::vector& CallLocalCandidates() { + ReturnCandidates return_value; + signaling_thread_->Send(this, kCallLocalCandidates, &return_value); + EXPECT_TRUE(return_value.return_value_ != NULL); + return *return_value.return_value_; + } + + void Initiate_s(talk_base::Message* message) { + ReturnBool* return_value = reinterpret_cast(message->pdata); + if (!session_->Initiate()) { + return_value->return_value_ = false; + return; + } + return_value->return_value_ = true; + } + + void Connect_s(talk_base::Message* message) { + ReturnBool* return_value = reinterpret_cast(message->pdata); + return_value->return_value_ = session_->Connect(); + } + + void OnRemoteDescription_s(talk_base::Message* message) { + ReturnBool* return_value = reinterpret_cast(message->pdata); + return_value->return_value_ = false; + std::vector candidates; + cricket::SessionDescription* description = GetLocalDescription(&candidates); + if (description == NULL) { + return; + } + if (!session_->OnRemoteDescription(description, candidates)) { + delete description; + return; + } + return_value->return_value_ = true; + } + + void OnInitiateMessage_s(talk_base::Message* message) { + cricket::SessionDescription* description = NULL; + std::vector candidates; + + ReturnBool* return_value = reinterpret_cast(message->pdata); + if (!GenerateFakeSession(false, &description, &candidates)) { + return_value->return_value_ = false; + return; + } + if (!session_->OnInitiateMessage(description, candidates)) { + return_value->return_value_ = false; + delete description; + return; + } + return_value->return_value_ = true; + } + + void OnMute_s(talk_base::Message* message) { + PassBool* return_value = reinterpret_cast(message->pdata); + session_->OnMute(return_value->argument()); + } + + void OnCameraMute_s(talk_base::Message* message) { + PassBool* return_value = reinterpret_cast(message->pdata); + session_->OnCameraMute(return_value->argument()); + } + + void Muted_s(talk_base::Message* message) { + ReturnBool* return_value = reinterpret_cast(message->pdata); + return_value->return_value_ = session_->muted(); + } + + void CameraMuted_s(talk_base::Message* message) { + ReturnBool* return_value = reinterpret_cast(message->pdata); + return_value->return_value_ = session_->camera_muted(); + } + + void CreateVoiceChannel_s(talk_base::Message* message) { + ReturnBoolPassString* return_value = + reinterpret_cast(message->pdata); + return_value->return_value_ = session_->CreateVoiceChannel( + return_value->argument()); + } + + void CreateVideoChannel_s(talk_base::Message* message) { + ReturnBoolPassString* return_value = + reinterpret_cast(message->pdata); + return_value->return_value_ = session_->CreateVideoChannel( + return_value->argument()); + } + + void RemoveStream_s(talk_base::Message* message) { + ReturnBoolPassString* return_value = + reinterpret_cast(message->pdata); + return_value->return_value_ = session_->RemoveStream( + return_value->argument()); + } + + void RemoveAllStreams_s(talk_base::Message* message) { + EXPECT_TRUE(message->pdata == NULL); + session_->RemoveAllStreams(); + } + + void HasStreamString_s(talk_base::Message* message) { + ReturnBoolPassString* return_value = + reinterpret_cast(message->pdata); + return_value->return_value_ = session_->HasStream(return_value->argument()); + } + + void HasStreamBool_s(talk_base::Message* message) { + ReturnBoolPassBool* return_value = reinterpret_cast( + message->pdata); + return_value->return_value_ = session_->HasStream(return_value->argument()); + } + + void HasAudioStream_s(talk_base::Message* message) { + ReturnBool* return_value = reinterpret_cast(message->pdata); + return_value->return_value_ = session_->HasAudioStream(); + } + + void HasVideoStream_s(talk_base::Message* message) { + ReturnBool* return_value = reinterpret_cast(message->pdata); + return_value->return_value_ = session_->HasVideoStream(); + } + + void SetVideoRenderer_s(talk_base::Message* message) { + ReturnBoolPassStringVideoRenderer* return_value = + reinterpret_cast(message->pdata); + return_value->return_value_ = session_->SetVideoRenderer( + return_value->argument().first, return_value->argument().second); + } + + void LocalCandidates_s(talk_base::Message* message) { + ReturnCandidates* return_value = + reinterpret_cast(message->pdata); + return_value->return_value_ = &session_->local_candidates(); + } + + void OnMessage(talk_base::Message* message) { + if ((message->pdata == NULL) && + (message->message_id != kCallRemoveAllStreams) && + (message->message_id != kTerminate)) { + ADD_FAILURE(); + return; + } + if (!signaling_thread_->IsCurrent()) { + ADD_FAILURE(); + return; + } + + switch (message->message_id) { + case kCallInitiate: + Initiate_s(message); + return; + case kCallConnect: + Connect_s(message); + return; + case kCallOnRemoteDescription: + OnRemoteDescription_s(message); + return; + case kCallOnInitiateMessage: + OnInitiateMessage_s(message); + return; + case kCallOnMute: + OnMute_s(message); + return; + case kCallOnCameraMute: + OnCameraMute_s(message); + return; + case kCallMuted: + Muted_s(message); + return; + case kCallCameraMuted: + CameraMuted_s(message); + return; + case kCallCreateVoiceChannel: + CreateVoiceChannel_s(message); + return; + case kCallCreateVideoChannel: + CreateVideoChannel_s(message); + return; + case kCallRemoveStream: + RemoveStream_s(message); + return; + case kCallRemoveAllStreams: + RemoveAllStreams_s(message); + return; + case kCallHasStreamString: + HasStreamString_s(message); + return; + case kCallHasStreamBool: + HasStreamBool_s(message); + return; + case kCallHasAudioStream: + HasAudioStream_s(message); + return; + case kCallHasVideoStream: + HasVideoStream_s(message); + return; + case kCallSetVideoRenderer: + SetVideoRenderer_s(message); + return; + case kCallLocalCandidates: + LocalCandidates_s(message); + return; + case kInit: + Init_s(message); + return; + case kTerminate: + Terminate_s(); + return; + default: + ADD_FAILURE(); + return; + } + } + + private: + WebRTCSessionTest() + : session_(NULL), + id_(), + receiving_(false), + allocator_(NULL), + channel_manager_(NULL), + worker_thread_(NULL), + signaling_thread_(NULL) { + } + + webrtc::WebRTCSession* session_; + std::string id_; + bool receiving_; + + cricket::PortAllocator* allocator_; + + cricket::ChannelManager* channel_manager_; + + talk_base::Thread* worker_thread_; + talk_base::Thread* signaling_thread_; +}; + +bool CallbackReceived(WebRTCSessionTest* session, int timeout) { + Timing my_timer; + my_timer.IdleWait(timeout * 0.001); + const OnSignalImpl::CallbackId peek_id = + session->PeekOldestCallback(); + return peek_id != OnSignalImpl::kNone; +} + +void SleepMs(int timeout_ms) { + Timing my_timer; + my_timer.IdleWait(timeout_ms * 0.001); +} + +TEST(WebRtcSessionTest, InitializationReceiveSanity) { + const bool kReceiving = true; + talk_base::scoped_ptr my_session; + my_session.reset(WebRTCSessionTest::CreateWebRTCSessionTest(kReceiving)); + + ASSERT_TRUE(my_session.get() != NULL); + ASSERT_TRUE(my_session->CallInitiate()); + + // Should return false because no stream has been set up yet. + EXPECT_FALSE(my_session->CallConnect()); + const bool kVideo = true; + EXPECT_FALSE(my_session->CallHasStream(kVideo)); + EXPECT_FALSE(my_session->CallHasStream(!kVideo)); + + + EXPECT_EQ(OnSignalImpl::kNone, + my_session->PopOldestCallback()); +} + +TEST(WebRtcSessionTest, SendCallSetUp) { + const bool kReceiving = false; + talk_base::scoped_ptr my_session; + my_session.reset(WebRTCSessionTest::CreateWebRTCSessionTest(kReceiving)); + + ASSERT_TRUE(my_session.get() != NULL); + ASSERT_TRUE(my_session->CallInitiate()); + + ASSERT_TRUE(my_session->CallCreateVoiceChannel("Audio")); + ASSERT_TRUE(my_session->CallConnect()); + + ASSERT_TRUE(my_session->CallOnRemoteDescription()); + + // All callbacks should be caught by my session. Assert it. + ASSERT_FALSE(CallbackReceived(my_session.get(), 1000)); +} + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + // Added return_value so that it's convenient to put a breakpoint before + // exiting please note that the return value from RUN_ALL_TESTS() must + // be returned by the main function. + const int return_value = RUN_ALL_TESTS(); + return return_value; +} diff --git a/third_party_mods/libjingle/source/talk/app/webrtc_json.cc b/third_party_mods/libjingle/source/talk/app/webrtc_json.cc deleted file mode 100644 index c5a67810d..000000000 --- a/third_party_mods/libjingle/source/talk/app/webrtc_json.cc +++ /dev/null @@ -1,434 +0,0 @@ -/* - * libjingle - * Copyright 2004--2011, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -//this file contains all the json helper methods -#include "talk/app/webrtc_json.h" - -#include -#include - -#include "talk/base/json.h" -#include "talk/base/logging.h" -#include "talk/session/phone/mediasessionclient.h" -#include "talk/session/phone/codec.h" -#include "json/json.h" - -namespace webrtc { - -static const int kIceComponent = 1; -static const int kIceFoundation = 1; - -bool GetConnectionMediator(const Json::Value& value, std::string& connectionMediator) { - if (value.type() != Json::objectValue && value.type() != Json::nullValue) { - LOG(LS_WARNING) << "Failed to parse stun values" ; - return false; - } - - if (!GetStringFromJsonObject(value, "connectionmediator", &connectionMediator)) { - LOG(LS_WARNING) << "Failed to parse JSON for value: " - << value.toStyledString(); - return false; - } - return true; -} - -bool GetStunServer(const Json::Value& value, StunServiceDetails& stunServer) { - if (value.type() != Json::objectValue && value.type() != Json::nullValue) { - LOG(LS_WARNING) << "Failed to parse stun values" ; - return false; - } - - Json::Value stun; - if (GetValueFromJsonObject(value, "stun_service", &stun)) { - if (stun.type() == Json::objectValue) { - if (!GetStringFromJsonObject(stun, "host", &stunServer.host) || - !GetStringFromJsonObject(stun, "service", &stunServer.service) || - !GetStringFromJsonObject(stun, "protocol", &stunServer.protocol)) { - LOG(LS_WARNING) << "Failed to parse JSON value: " - << value.toStyledString(); - return false; - } - } else { - return false; - } - } - return true; - -} -bool GetTurnServer(const Json::Value& value, std::string& turnServer) { - if (value.type() != Json::objectValue && value.type() != Json::nullValue) { - LOG(LS_WARNING) << "Failed to parse stun values" ; - return false; - } - - Json::Value turn; - if (GetValueFromJsonObject(value, "turn_service", &turn)) { - if (turn.type() == Json::objectValue) { - if (!GetStringFromJsonObject(turn, "host", &turnServer)) { - LOG(LS_WARNING) << "Failed to parse JSON value: " - << value.toStyledString(); - return false; - } - } else { - return false; - } - } - return true; -} - -bool GetJSONSignalingMessage( - const cricket::SessionDescription* sdp, - const std::vector& candidates, - std::string* signaling_message) { - const cricket::ContentInfo* audio_content = GetFirstAudioContent(sdp); - const cricket::ContentInfo* video_content = GetFirstVideoContent(sdp); - - std::vector media; - if (audio_content) { - Json::Value value; - BuildMediaMessage(audio_content, candidates, false, value); - media.push_back(value); - } - - if (video_content) { - Json::Value value; - BuildMediaMessage(video_content, candidates, true, value); - media.push_back(value); - } - - Json::Value signal; - Append(signal, "media", media); - - // now serialize - *signaling_message = Serialize(signal); - return true; -} - -bool BuildMediaMessage( - const cricket::ContentInfo* content_info, - const std::vector& candidates, - bool video, - Json::Value& params) { - - if (!content_info) { - return false; - } - - if (video) { - Append(params, "label", 2); //always video 2 - } else { - Append(params, "label", 1); //always audio 1 - } - std::vector rtpmap; - - if (!BuildRtpMapParams(content_info, video, rtpmap)) { - return false; - } - - Append(params, "rtpmap", rtpmap); - - Json::Value attributes; -// Append(attributes, "ice-pwd", candidates.front().password()); -// Append(attributes, "ice-ufrag", candidates.front().username()); - std::vector jcandidates; - - if (!BuildAttributes(candidates, video, jcandidates)) { - return false; - } - Append(attributes, "candidate", jcandidates); - Append(params, "attributes", attributes); - return true; -} - -bool BuildRtpMapParams(const cricket::ContentInfo* content_info, - bool video, - std::vector& rtpmap) { - - if (!video) { - const cricket::AudioContentDescription* audio_offer = - static_cast( - content_info->description); - - - for (std::vector::const_iterator iter = - audio_offer->codecs().begin(); - iter != audio_offer->codecs().end(); ++iter) { - - Json::Value codec; - std::string codec_str = std::string("audio/").append(iter->name); - Append(codec, "codec", codec_str); - Json::Value codec_id; - Append(codec_id, talk_base::ToString(iter->id), codec); - rtpmap.push_back(codec_id); - } - } else { - const cricket::VideoContentDescription* video_offer = - static_cast( - content_info->description); - - - for (std::vector::const_iterator iter = - video_offer->codecs().begin(); - iter != video_offer->codecs().end(); ++iter) { - - Json::Value codec; - std::string codec_str = std::string("video/").append(iter->name); - Append(codec, "codec", codec_str); - Json::Value codec_id; - Append(codec_id, talk_base::ToString(iter->id), codec); - rtpmap.push_back(codec_id); - } - } - return true; -} - -bool BuildAttributes(const std::vector& candidates, - bool video, - std::vector& jcandidates) { - - for (std::vector::const_iterator iter = - candidates.begin(); iter != candidates.end(); ++iter) { - if ((video && !iter->name().compare("video_rtp") || - (!video && !iter->name().compare("rtp")))) { - Json::Value candidate; - Append(candidate, "component", kIceComponent); - Append(candidate, "foundation", kIceFoundation); - Append(candidate, "generation", iter->generation()); - Append(candidate, "proto", iter->protocol()); - Append(candidate, "priority", iter->preference()); - Append(candidate, "ip", iter->address().IPAsString()); - Append(candidate, "port", iter->address().PortAsString()); - Append(candidate, "type", iter->type()); - Append(candidate, "name", iter->name()); - Append(candidate, "network_name", iter->network_name()); - Append(candidate, "username", iter->username()); - Append(candidate, "password", iter->password()); - jcandidates.push_back(candidate); - } - } - return true; -} - -std::string Serialize(const Json::Value& value) { - Json::StyledWriter writer; - return writer.write(value); -} - -bool Deserialize(const std::string& message, Json::Value& value) { - Json::Reader reader; - return reader.parse(message, value); -} - - -bool ParseJSONSignalingMessage(const std::string& signaling_message, - cricket::SessionDescription*& sdp, - std::vector& candidates) { - ASSERT(!sdp); // expect this to NULL - // first deserialize message - Json::Value value; - if (!Deserialize(signaling_message, value)) { - return false; - } - - // get media objects - std::vector mlines = ReadValues(value, "media"); - if (mlines.empty()) { - // no m-lines found - return false; - } - - sdp = new cricket::SessionDescription(); - - // get codec information - for (size_t i = 0; i < mlines.size(); ++i) { - if (mlines[i]["label"].asInt() == 1) { - cricket::AudioContentDescription* audio_content = - new cricket::AudioContentDescription(); - ParseAudioCodec(mlines[i], audio_content); - audio_content->SortCodecs(); - sdp->AddContent(cricket::CN_AUDIO, cricket::NS_JINGLE_RTP, audio_content); - ParseICECandidates(mlines[i], candidates); - - } else { - cricket::VideoContentDescription* video_content = - new cricket::VideoContentDescription(); - ParseVideoCodec(mlines[i], video_content); - video_content->SortCodecs(); - sdp->AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP, video_content); - ParseICECandidates(mlines[i], candidates); - } - } - return true; -} - -bool ParseAudioCodec(Json::Value value, - cricket::AudioContentDescription* content) { - std::vector rtpmap(ReadValues(value, "rtpmap")); - if (rtpmap.empty()) - return false; - - for (size_t i = 0; i < rtpmap.size(); ++i) { - cricket::AudioCodec codec; - std::string pltype = rtpmap[i].begin().memberName(); - talk_base::FromString(pltype, &codec.id); - Json::Value codec_info = rtpmap[i][pltype]; - std::vector tokens; - talk_base::split(codec_info["codec"].asString(), '/', &tokens); - codec.name = tokens[1]; - content->AddCodec(codec); - } - - return true; -} - -bool ParseVideoCodec(Json::Value value, - cricket::VideoContentDescription* content) { - std::vector rtpmap(ReadValues(value, "rtpmap")); - if (rtpmap.empty()) - return false; - - for (size_t i = 0; i < rtpmap.size(); ++i) { - cricket::VideoCodec codec; - std::string pltype = rtpmap[i].begin().memberName(); - talk_base::FromString(pltype, &codec.id); - Json::Value codec_info = rtpmap[i][pltype]; - std::vector tokens; - talk_base::split(codec_info["codec"].asString(), '/', &tokens); - codec.name = tokens[1]; - content->AddCodec(codec); - } - return true; -} - -bool ParseICECandidates(Json::Value& value, - std::vector& candidates) { - Json::Value attributes = ReadValue(value, "attributes"); - std::string ice_pwd = ReadString(attributes, "ice-pwd"); - std::string ice_ufrag = ReadString(attributes, "ice-ufrag"); - - std::vector jcandidates = ReadValues(attributes, "candidate"); - char buffer[64]; - for (size_t i = 0; i < jcandidates.size(); ++i) { - cricket::Candidate cand; - std::string str; - str = ReadUInt(jcandidates[i], "generation"); - cand.set_generation_str(str); - str = ReadString(jcandidates[i], "proto"); - cand.set_protocol(str); - double priority = ReadDouble(jcandidates[i], "priority"); -#ifdef _DEBUG - double as_int = static_cast(priority); - ASSERT(as_int == priority); -#endif - sprintf(buffer, "%i", static_cast(priority)); - str = buffer; - cand.set_preference_str(str); - talk_base::SocketAddress addr; - str = ReadString(jcandidates[i], "ip"); - addr.SetIP(str); - str = ReadString(jcandidates[i], "port"); - int port; talk_base::FromString(str, &port); - addr.SetPort(port); - cand.set_address(addr); - str = ReadString(jcandidates[i], "type"); - cand.set_type(str); - str = ReadString(jcandidates[i], "name"); - cand.set_name(str); - str = ReadString(jcandidates[i], "network_name"); - cand.set_network_name(str); - str = ReadString(jcandidates[i], "username"); - cand.set_username(str); - str = ReadString(jcandidates[i], "password"); - cand.set_password(str); - candidates.push_back(cand); - } - return true; -} - -std::vector ReadValues( - Json::Value& value, const std::string& key) { - std::vector objects; - for (size_t i = 0; i < value[key].size(); ++i) { - objects.push_back(value[key][i]); - } - return objects; -} - -Json::Value ReadValue(Json::Value& value, const std::string& key) { - return value[key]; -} - -std::string ReadString(Json::Value& value, const std::string& key) { - return value[key].asString(); -} - -uint32 ReadUInt(Json::Value& value, const std::string& key) { - return value[key].asUInt(); -} - -double ReadDouble(Json::Value& value, const std::string& key) { - return value[key].asDouble(); -} - -// Add values -void Append(Json::Value& object, const std::string& key, bool value) { - object[key] = Json::Value(value); -} - -void Append(Json::Value& object, const std::string& key, char * value) { - object[key] = Json::Value(value); -} -void Append(Json::Value& object, const std::string& key, double value) { - object[key] = Json::Value(value); -} -void Append(Json::Value& object, const std::string& key, float value) { - object[key] = Json::Value(value); -} -void Append(Json::Value& object, const std::string& key, int value) { - object[key] = Json::Value(value); -} -void Append(Json::Value& object, const std::string& key, std::string value) { - object[key] = Json::Value(value); -} -void Append(Json::Value& object, const std::string& key, uint32 value) { - object[key] = Json::Value(value); -} - -void Append(Json::Value& object, const std::string& key, Json::Value value) { - object[key] = value; -} - -void Append(Json::Value & object, - const std::string & key, - std::vector& values){ - for (std::vector::const_iterator iter = values.begin(); - iter != values.end(); ++iter) { - object[key].append(*iter); - } -} - -} //namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/webrtcsession.h b/third_party_mods/libjingle/source/talk/app/webrtcsession.h deleted file mode 100644 index 17a33d6f4..000000000 --- a/third_party_mods/libjingle/source/talk/app/webrtcsession.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * libjingle - * Copyright 2004--2011, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_APP_WEBRTC_WEBRTCSESSION_H_ -#define TALK_APP_WEBRTC_WEBRTCSESSION_H_ - -#include "talk/base/logging.h" -#include "talk/p2p/base/constants.h" -#include "talk/p2p/base/session.h" - -namespace cricket { -class PortAllocator; -} - -namespace webrtc { -class PeerConnection; - -class WebRTCSession: public cricket::BaseSession { - public: - WebRTCSession(const std::string& id, const std::string& direction, - cricket::PortAllocator* allocator, - PeerConnection* connection, - talk_base::Thread* signaling_thread) - : BaseSession(signaling_thread), - signaling_thread_(signaling_thread), - id_(id), - incoming_(direction == kIncomingDirection), - port_allocator_(allocator), - connection_(connection) { - BaseSession::sid_ = id; - } - - virtual ~WebRTCSession() { - } - - virtual bool Initiate() = 0; - - const std::string& id() const { return id_; } - //const std::string& type() const { return type_; } - bool incoming() const { return incoming_; } - cricket::PortAllocator* port_allocator() const { return port_allocator_; } - -// static const std::string kAudioType; -// static const std::string kVideoType; - static const std::string kIncomingDirection; - static const std::string kOutgoingDirection; -// static const std::string kTestType; - PeerConnection* connection() const { return connection_; } - - protected: - //methods from cricket::BaseSession - virtual bool Accept(const cricket::SessionDescription* sdesc) { - return true; - } - virtual bool Reject(const std::string& reason) { - return true; - } - virtual bool TerminateWithReason(const std::string& reason) { - return true; - } - - protected: - talk_base::Thread* signaling_thread_; - - private: - std::string id_; - //std::string type_; - bool incoming_; - cricket::PortAllocator* port_allocator_; - PeerConnection* connection_; -}; - -} // namespace webrtc - - -#endif /* TALK_APP_WEBRTC_WEBRTCSESSION_H_ */ diff --git a/third_party_mods/libjingle/source/talk/app/webrtcsessionimpl.cc b/third_party_mods/libjingle/source/talk/app/webrtcsessionimpl.cc deleted file mode 100644 index f71f6aaae..000000000 --- a/third_party_mods/libjingle/source/talk/app/webrtcsessionimpl.cc +++ /dev/null @@ -1,1067 +0,0 @@ -/* - * libjingle - * Copyright 2004--2011, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "talk/app/webrtcsessionimpl.h" - -#include -#include - -#include "talk/app/pc_transport_impl.h" -#include "talk/app/peerconnection.h" -#include "talk/app/webrtc_json.h" -#include "talk/base/common.h" -#include "talk/base/json.h" -#include "talk/base/scoped_ptr.h" -#include "talk/p2p/base/constants.h" -#include "talk/p2p/base/sessiondescription.h" -#include "talk/p2p/base/p2ptransport.h" -#include "talk/session/phone/channel.h" -#include "talk/session/phone/channelmanager.h" -#include "talk/session/phone/mediasessionclient.h" -#include "talk/session/phone/voicechannel.h" - -using namespace cricket; - -namespace webrtc { - -enum { - MSG_RTC_CREATEVIDEOCHANNEL = 1, - MSG_RTC_CREATEAUDIOCHANNEL = 2, - MSG_RTC_SETSTATE = 3, - MSG_RTC_SETVIDEOCAPTURE = 4, - MSG_RTC_CANDIDATETIMEOUT = 5, - MSG_RTC_SETEXTERNALRENDERER = 6, - MSG_RTC_SETCRICKETRENDERER = 7, - MSG_RTC_CHANNELENABLE = 8, - MSG_RTC_SIGNALONWRITABLESTATE = 9, - MSG_RTC_DESTROYVOICECHANNEL = 10, - MSG_RTC_DESTROYVIDEOCHANNEL = 11, - MSG_RTC_SENDLOCALDESCRIPTION = 12, - MSG_RTC_REMOVESTREAM = 13, - MSG_RTC_REMOVEALLSTREAMS = 14, - MSG_RTC_ENABLEALLSTREAMS = 15, - MSG_RTC_SETSESSIONERROR = 16, -}; - -struct CreateChannelParams : public talk_base::MessageData { - CreateChannelParams(const std::string& content_name, bool rtcp, - cricket::VoiceChannel* voice_channel) - : content_name(content_name), - rtcp(rtcp), - voice_channel(voice_channel), - video_channel(NULL) {} - - std::string content_name; - bool rtcp; - cricket::VoiceChannel* voice_channel; - cricket::VideoChannel* video_channel; -}; - -struct SetStateParams : public talk_base::MessageData { - SetStateParams(int state) - : state(state) {} - int state; - bool result; -}; - -struct CaptureParams : public talk_base::MessageData { - explicit CaptureParams(bool c) : capture(c), result(CR_FAILURE) {} - - bool capture; - CaptureResult result; -}; - -struct ExternalRenderParams : public talk_base::MessageData { - ExternalRenderParams(const std::string& stream_id, - ExternalRenderer* external_renderer) - : stream_id(stream_id), - external_renderer(external_renderer), - result(false) {} - - const std::string stream_id; - ExternalRenderer* external_renderer; - bool result; -}; - -struct CricketRenderParams : public talk_base::MessageData { - CricketRenderParams(const std::string& stream_id, - cricket::VideoRenderer* renderer) - : stream_id(stream_id), - renderer(renderer), - result(false) {} - - const std::string stream_id; - cricket::VideoRenderer* renderer; - bool result; -}; - -struct ChannelEnableParams : public talk_base::MessageData { - ChannelEnableParams(cricket::BaseChannel* channel, bool enable) - : channel(channel), enable(enable) {} - - cricket::BaseChannel* channel; - bool enable; -}; - -static const int kAudioMonitorPollFrequency = 100; -static const int kMonitorPollFrequency = 1000; - -// We allow 30 seconds to establish a connection; beyond that we consider -// it an error -static const int kCallSetupTimeout = 30 * 1000; -// A loss of connectivity is probably due to the Internet connection going -// down, and it might take a while to come back on wireless networks, so we -// use a longer timeout for that. -static const int kCallLostTimeout = 60 * 1000; -static const uint32 kCandidateTimeoutId = 101; - -typedef std::vector StreamMap; // not really a map (vector) - -WebRTCSessionImpl::WebRTCSessionImpl( - const std::string& id, - const std::string& direction, - cricket::PortAllocator* allocator, - cricket::ChannelManager* channelmgr, - PeerConnection* connection, - talk_base::Thread* signaling_thread) - : WebRTCSession(id, direction, allocator, connection, signaling_thread), - channel_manager_(channelmgr), - all_writable_(false), - muted_(false), - camera_muted_(false), - setup_timeout_(kCallSetupTimeout), - signal_initiated_(false) { -} - -WebRTCSessionImpl::~WebRTCSessionImpl() { - if (state_ != STATE_RECEIVEDTERMINATE) { - Terminate(); - } -} - -bool WebRTCSessionImpl::CreateP2PTransportChannel(const std::string& stream_id, - bool video) { - PC_Transport_Impl* transport = new PC_Transport_Impl(this); - ASSERT(transport != NULL); - const std::string name = ((video) ? "video_rtp" : "rtp"); - if (!transport->Init(name)) { - delete transport; - return false; - } - - ASSERT(transport_channels_.find(name) == transport_channels_.end()); - transport_channels_[name] = transport; - - StreamInfo* stream_info = new StreamInfo(stream_id); - stream_info->transport = transport; - stream_info->video = video; - streams_.push_back(stream_info); - - return true; -} - -bool WebRTCSessionImpl::CreateVoiceChannel(const std::string& stream_id) { - this->SignalVoiceChannel.connect( - this, &WebRTCSessionImpl::OnVoiceChannelCreated); - - signaling_thread_->Post(this, MSG_RTC_CREATEAUDIOCHANNEL, - new CreateChannelParams(stream_id, true, NULL)); - return true; -} - -cricket::VoiceChannel* WebRTCSessionImpl::CreateVoiceChannel_w( - const std::string& content_name, - bool rtcp) { - cricket::VoiceChannel* voice_channel = channel_manager_->CreateVoiceChannel( - this, content_name, rtcp); - return voice_channel; -} - -void WebRTCSessionImpl::OnVoiceChannelCreated( - cricket::VoiceChannel* voice_channel, - std::string& stream_id) { - StreamMap::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - StreamInfo* stream_info = (*iter); - if (stream_info->stream_id.compare(stream_id) == 0) { - ASSERT(stream_info->channel == NULL); - stream_info->channel = voice_channel; - stream_info->media_channel = - voice_channel->media_channel()->GetMediaChannelId(); - if (incoming()) { - // change stream id to audio- - // ^^ - The code that does this has been disabled because - // it causes us to not be able to find the stream by name later. - // Instead, we could store the channel_id as an int member with - // stream_info? - streams_.erase(iter); -#if 0 - stream_info->stream_id.append("-"); - stream_info->stream_id.append( - talk_base::ToString(stream_info->media_channel)); -#endif - streams_.push_back(stream_info); - connection()->OnAddStream( - stream_info->stream_id, stream_info->media_channel, false); - } else { - connection()->OnRtcMediaChannelCreated( - stream_id, stream_info->media_channel, false); - } - break; - } - } -} - -bool WebRTCSessionImpl::CreateVideoChannel(const std::string& stream_id) { - this->SignalVideoChannel.connect( - this, &WebRTCSessionImpl::OnVideoChannelCreated); - - signaling_thread_->Post(this, MSG_RTC_CREATEVIDEOCHANNEL, - new CreateChannelParams(stream_id, true, NULL)); - return true; -} - -cricket::VideoChannel* WebRTCSessionImpl::CreateVideoChannel_w( - const std::string& content_name, - bool rtcp, - cricket::VoiceChannel* voice_channel) { - cricket::VideoChannel* video_channel = channel_manager_->CreateVideoChannel( - this, content_name, rtcp, voice_channel); - return video_channel; -} - -void WebRTCSessionImpl::OnVideoChannelCreated( - cricket::VideoChannel* video_channel, - std::string& stream_id) { - StreamMap::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - StreamInfo* stream_info = (*iter); - if (stream_info->stream_id.compare(stream_id) == 0) { - ASSERT(stream_info->channel == NULL); - stream_info->channel = video_channel; - stream_info->media_channel = - video_channel->media_channel()->GetMediaChannelId(); - if (incoming()) { - // change stream id to video- - // ^^ - The code that does this has been disabled because - // it causes us to not be able to find the stream by name later. - // Instead, we could store the channel_id as an int member with - // stream_info? - streams_.erase(iter); -#if 0 - stream_info->stream_id.append("-"); - stream_info->stream_id.append( - talk_base::ToString(stream_info->media_channel)); -#endif - streams_.push_back(stream_info); - connection()->OnAddStream( - stream_info->stream_id, stream_info->media_channel, true); - } else { - connection()->OnRtcMediaChannelCreated( - stream_id, stream_info->media_channel, true); - } - break; - } - } -} - -bool WebRTCSessionImpl::SetVideoRenderer(const std::string& stream_id, - cricket::VideoRenderer* renderer) { - if(signaling_thread_ != talk_base::Thread::Current()) { - signaling_thread_->Post(this, MSG_RTC_SETCRICKETRENDERER, - new CricketRenderParams(stream_id, renderer), - true); - return true; - } - - ASSERT(signaling_thread_ == talk_base::Thread::Current()); - - bool ret = false; - StreamMap::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - StreamInfo* stream_info = (*iter); - if (stream_info->stream_id.compare(stream_id) == 0) { - ASSERT(stream_info->channel != NULL); - ASSERT(stream_info->video); - cricket::VideoChannel* channel = static_cast( - stream_info->channel); - ret = channel->SetRenderer(0, renderer); - break; - } - } - return ret; -} - -bool WebRTCSessionImpl::SetVideoRenderer(const std::string& stream_id, - ExternalRenderer* external_renderer) { - if(signaling_thread_ != talk_base::Thread::Current()) { - signaling_thread_->Post(this, MSG_RTC_SETEXTERNALRENDERER, - new ExternalRenderParams(stream_id, external_renderer), - true); - return true; - } - - ASSERT(signaling_thread_ == talk_base::Thread::Current()); - - bool ret = false; - StreamMap::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - StreamInfo* stream_info = (*iter); - if (stream_info->stream_id.compare(stream_id) == 0) { - ASSERT(stream_info->channel != NULL); - ASSERT(stream_info->video); - cricket::VideoChannel* channel = static_cast ( - stream_info->channel); - ret = channel->media_channel()->SetExternalRenderer(0, external_renderer); - break; - } - } - return ret; -} - -void WebRTCSessionImpl::OnMessage(talk_base::Message* message) { - using talk_base::TypedMessageData; - talk_base::MessageData* data = message->pdata; - switch(message->message_id) { - case MSG_RTC_CREATEVIDEOCHANNEL: { - CreateChannelParams* p = reinterpret_cast(data); - p->video_channel = - CreateVideoChannel_w(p->content_name, p->rtcp, p->voice_channel); - SignalVideoChannel(p->video_channel, p->content_name); - delete p; - break; - } - case MSG_RTC_CREATEAUDIOCHANNEL: { - CreateChannelParams* p = reinterpret_cast(data); - p->voice_channel = - CreateVoiceChannel_w(p->content_name, p->rtcp); - SignalVoiceChannel(p->voice_channel, p->content_name); - delete p; - break; - } - case MSG_RTC_DESTROYVOICECHANNEL: { - cricket::VoiceChannel* channel = - reinterpret_cast*>(data) - ->data(); - std::string name(channel->content_name()); - DestroyVoiceChannel_w(channel); - delete data; - break; - } - case MSG_RTC_SETSESSIONERROR: { - int err = reinterpret_cast*>(data)->data(); - BaseSession::SetError(static_cast(err)); - delete data; - break; - } - case MSG_RTC_DESTROYVIDEOCHANNEL: { - cricket::VideoChannel* channel = - reinterpret_cast*>(data) - ->data(); - std::string name(channel->content_name()); - DestroyVideoChannel_w(channel); - delete data; - break; - } - case MSG_RTC_REMOVESTREAM : { - std::string stream_id( - reinterpret_cast*>(data)->data()); - RemoveStream_w(stream_id); - delete data; - break; - } - case MSG_RTC_REMOVEALLSTREAMS : { - RemoveAllStreams_w(); - delete data; - break; - } - case MSG_RTC_ENABLEALLSTREAMS: { - EnableAllStreams_w(); - delete data; - break; - } - case MSG_RTC_SETSTATE : { - SetSessionState_w(); - break; - } - case MSG_RTC_SETVIDEOCAPTURE : { - CaptureParams* p = reinterpret_cast(data); - p->result = SetVideoCapture_w(p->capture); - delete p; - break; - } - case MSG_RTC_SETEXTERNALRENDERER : { - ExternalRenderParams* p = reinterpret_cast(data); - p->result = SetVideoRenderer(p->stream_id, p->external_renderer); - delete p; - break; - } - case MSG_RTC_SETCRICKETRENDERER : { - CricketRenderParams* p = reinterpret_cast(data); - p->result = SetVideoRenderer(p->stream_id, p->renderer); - delete p; - break; - } - case MSG_RTC_CHANNELENABLE : { - ChannelEnableParams* p = reinterpret_cast(data); - ChannelEnable_w(p->channel, p->enable); - delete p; - break; - } - case MSG_RTC_SIGNALONWRITABLESTATE : { - cricket::TransportChannel* channel = - reinterpret_cast*>(data) - ->data(); - SignalOnWritableState_w(channel); - delete data; - break; - } - case MSG_RTC_CANDIDATETIMEOUT: { - break; - } - case MSG_RTC_SENDLOCALDESCRIPTION : { - SendLocalDescription_w(); - break; - } - default: { - WebRTCSession::OnMessage(message); - } - } -} - -bool WebRTCSessionImpl::Initiate() { - if (streams_.empty()) { - // nothing to initiate - return false; - } - - // Enable all the channels - signaling_thread_->Post(this, MSG_RTC_ENABLEALLSTREAMS); - - SetVideoCapture(true); - signal_initiated_ = true; - - if (local_candidates_.size() == streams_.size()) { - SendLocalDescription(); - } - return true; -} - -void WebRTCSessionImpl::ChannelEnable(cricket::BaseChannel* channel, - bool enable) { - ASSERT(channel); - signaling_thread_->Post(this, MSG_RTC_CHANNELENABLE, - new ChannelEnableParams(channel, enable), true); -} - -void WebRTCSessionImpl::ChannelEnable_w(cricket::BaseChannel* channel, - bool enable) { - if (channel) { - channel->Enable(enable); - } -} - -void WebRTCSessionImpl::SetSessionState(State state) { - session_state_ = state; - signaling_thread_->Post(this, MSG_RTC_SETSTATE); -} - -void WebRTCSessionImpl::SetSessionState_w() { - SetState(session_state_); -} - -bool WebRTCSessionImpl::SetVideoCapture(bool capture) { - signaling_thread_->Post(this, MSG_RTC_SETVIDEOCAPTURE, - new CaptureParams(capture), true); - return true; -} - -cricket::CaptureResult WebRTCSessionImpl::SetVideoCapture_w(bool capture) { - ASSERT(signaling_thread_ == talk_base::Thread::Current()); - return channel_manager_->SetVideoCapture(capture); -} - -void WebRTCSessionImpl::OnVoiceChannelError( - cricket::VoiceChannel* voice_channel, uint32 ssrc, - cricket::VoiceMediaChannel::Error error) { - //report error to connection -} - -void WebRTCSessionImpl::OnVideoChannelError( - cricket::VideoChannel* video_channel, uint32 ssrc, - cricket::VideoMediaChannel::Error error) { - //report error to connection -} - -void WebRTCSessionImpl::RemoveStream_w(const std::string& stream_id) { - bool found = false; - StreamMap::iterator iter; - std::string candidate_name; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - StreamInfo* sinfo = (*iter); - candidate_name = sinfo->transport->name(); - if (sinfo->stream_id.compare(stream_id) == 0) { - DisableLocalCandidate(candidate_name); - if (!sinfo->video) { - cricket::VoiceChannel* channel = static_cast ( - sinfo->channel); - channel_manager_->DestroyVoiceChannel(channel); - } else { - cricket::VideoChannel* channel = static_cast ( - sinfo->channel); - channel_manager_->DestroyVideoChannel(channel); - } - // channel and transport will be deleted in - // DestroyVoiceChannel/DestroyVideoChannel - found = true; - break; - } - } - if (!found) { - LOG(LS_ERROR) << "No streams found for stream id " << stream_id; - //TODO - trigger onError callback - } -} - -bool WebRTCSessionImpl::RemoveStream(const std::string& stream_id) { - bool ret = true; - if ((state_ == STATE_RECEIVEDACCEPT) || - (state_ == STATE_SENTACCEPT)) { - - signaling_thread_->Post(this, MSG_RTC_REMOVESTREAM, - new talk_base::TypedMessageData(stream_id)); - } else { - LOG(LS_ERROR) << "Invalid session state -" << state_; - ret = false; - } - return ret; -} - -void WebRTCSessionImpl::DisableLocalCandidate(const std::string& name) { - for (size_t i = 0; i < local_candidates_.size(); ++i) { - if (local_candidates_[i].name().compare(name) == 0) { - talk_base::SocketAddress address(local_candidates_[i].address().ip(), 0); - local_candidates_[i].set_address(address); - } - } -} - -void WebRTCSessionImpl::RemoveAllStreams_w() { - // First build a list of streams to remove and then remove them. - // The reason we do this is that if we remove the streams inside the - // loop, a stream might get removed while we're enumerating and the iterator - // will become invalid (and we crash). - std::vector streams_to_remove; - StreamMap::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) - streams_to_remove.push_back((*iter)->stream_id); - - for (std::vector::iterator i = streams_to_remove.begin(); - i != streams_to_remove.end(); ++i) - RemoveStream_w(*i); - - SignalOnRemoveStream(this); -} - -void WebRTCSessionImpl::EnableAllStreams_w() { - StreamMap::const_iterator i; - for (i = streams_.begin(); i != streams_.end(); ++i) { - cricket::BaseChannel* channel = (*i)->channel; - if (channel) - channel->Enable(true); - } -} - -void WebRTCSessionImpl::RemoveAllStreams() { - signaling_thread_->Post(this, MSG_RTC_REMOVEALLSTREAMS); -} - -bool WebRTCSessionImpl::HasStream(const std::string& stream_id) const { - StreamMap::const_iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - StreamInfo* sinfo = (*iter); - if (stream_id.compare(sinfo->stream_id) == 0) { - return true; - } - } - return false; -} - -bool WebRTCSessionImpl::HasStream(bool video) const { - StreamMap::const_iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - StreamInfo* sinfo = (*iter); - if (sinfo->video == video) { - return true; - } - } - return false; -} - -bool WebRTCSessionImpl::HasAudioStream() const { - return HasStream(false); -} - -bool WebRTCSessionImpl::HasVideoStream() const { - return HasStream(true); -} - -void WebRTCSessionImpl::OnRequestSignaling(cricket::Transport* transport) { - transport->OnSignalingReady(); -} - -cricket::TransportChannel* WebRTCSessionImpl::CreateChannel( - const std::string& content_name, const std::string& name) { - - // channel must be already present in the vector - StreamMap::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - if (content_name.compare((*iter)->stream_id) == 0) { - StreamInfo* sinfo = (*iter); - // if it's a incoming call, remote candidates are already received - // in initial SignalingMessage. apply now - if (incoming() && state_ == STATE_RECEIVEDINITIATE) { - // process the remote candidates - std::vector::iterator iter; - for (iter = remote_candidates_.begin(); - iter != remote_candidates_.end(); ++iter) { - std::string tname = iter->name(); - TransportChannelMap::iterator titer = transport_channels_.find(tname); - if (titer != transport_channels_.end()) { - titer->second->AddRemoteCandidate(*iter); - } - } - } - return sinfo->transport->GetP2PChannel(); - } - } - return NULL; -} - -cricket::TransportChannel* WebRTCSessionImpl::GetChannel( - const std::string& content_name, const std::string& name) { - StreamMap::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - if (content_name.compare((*iter)->stream_id) == 0) { - PC_Transport_Impl* transport = (*iter)->transport; - return transport->GetP2PChannel(); - } - } - return NULL; -} - -void WebRTCSessionImpl::DestroyChannel( - const std::string& content_name, const std::string& name) { - bool found = false; - StreamMap::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - if (content_name.compare((*iter)->stream_id) == 0) { - PC_Transport_Impl* transport = (*iter)->transport; - delete transport; - (*iter)->transport = NULL; - connection()->OnRemoveStream((*iter)->stream_id, (*iter)->media_channel, - (*iter)->video); - streams_.erase(iter); - found = true; - break; - } - } -} - -void WebRTCSessionImpl::DestroyVoiceChannel_w( - cricket::VoiceChannel* channel) { - channel_manager_->DestroyVoiceChannel(channel); -} - -void WebRTCSessionImpl::DestroyVideoChannel_w( - cricket::VideoChannel* channel) { - channel_manager_->DestroyVideoChannel(channel); - -} - -void WebRTCSessionImpl::StartTransportTimeout(int timeout) { - talk_base::Thread::Current()->PostDelayed(timeout, this, - MSG_RTC_CANDIDATETIMEOUT, - NULL); -} - -void WebRTCSessionImpl::ClearTransportTimeout() { - //LOG(LS_INFO) << "ClearTransportTimeout"; - talk_base::Thread::Current()->Clear(this, MSG_RTC_CANDIDATETIMEOUT); -} - -void WebRTCSessionImpl::NotifyTransportState() { -} - -bool WebRTCSessionImpl::OnRemoteDescription(Json::Value& desc) { - if ((!incoming() && state() != STATE_SENTINITIATE) || - (incoming() && state() != STATE_INIT)) { - LOG(LS_WARNING) << "Invalid session state" ; - return false; - } - - talk_base::scoped_ptr audio( - new cricket::AudioContentDescription()); - - talk_base::scoped_ptr video( - new cricket::VideoContentDescription()); - - //TODO- get media description from Json format - //set_remote_description(); - - if (incoming()) { - SetState(STATE_RECEIVEDINITIATE); - } - return true; -} - -bool WebRTCSessionImpl::OnInitiateMessage( - const cricket::SessionDescription* offer, - std::vector& candidates) { - if (!offer) { - LOG(LS_ERROR) << "No SessionDescription from peer"; - return false; - } - - set_remote_description(offer); - const cricket::SessionDescription* answer = CreateAnswer(offer); - - const cricket::ContentInfo* audio_content = GetFirstAudioContent(answer); - const cricket::ContentInfo* video_content = GetFirstVideoContent(answer); - - if (!audio_content && !video_content) { - // no codec information of audio and video - set_remote_description(NULL); - delete answer; - return false; - } - - SetSessionState(STATE_RECEIVEDINITIATE); - - bool ret = true; - if (audio_content) { - ret = !HasAudioStream() && - CreateP2PTransportChannel(audio_content->name, false) && - CreateVoiceChannel(audio_content->name); - } - - if (video_content) { - ret = !HasVideoStream() && - CreateP2PTransportChannel(video_content->name, true) && - CreateVideoChannel(video_content->name); - } - - delete answer; - - if (!ret) { - LOG(LS_ERROR) << "Failed to create channel for incoming media stream"; - return false; - } - - // Candidate processing. - ASSERT(candidates.size()); - remote_candidates_.clear(); - remote_candidates_.insert(remote_candidates_.begin(), - candidates.begin(), candidates.end()); - return true; -} - -bool WebRTCSessionImpl::OnRemoteDescription( - const cricket::SessionDescription* rdesc, - std::vector& candidates) { - - if (state() == STATE_SENTACCEPT || state() == STATE_RECEIVEDACCEPT) { - return OnRemoteDescriptionUpdate(rdesc, candidates); - } - - if ((!incoming()) && (state() != STATE_SENTINITIATE)) { - LOG(LS_ERROR) << "invalid session state"; - return false; - } - -// cricket::SessionDescription* answer = new cricket::SessionDescription(); -// const ContentInfo* audio_content = GetFirstAudioContent(rdesc); -// if (audio_content) { -// const AudioContentDescription* audio_offer = -// static_cast(audio_content->description); -// -// AudioContentDescription* audio_accept = new AudioContentDescription(); -// -// -// for (AudioCodecs::const_iterator theirs = audio_offer->codecs().begin(); -// theirs != audio_offer->codecs().end(); ++theirs) { -// audio_accept->AddCodec(*theirs); -// } -// audio_accept->SortCodecs(); -// answer->AddContent(audio_content->name, audio_content->type, audio_accept); -// } -// -// const ContentInfo* video_content = GetFirstVideoContent(rdesc); -// if (video_content) { -// const VideoContentDescription* video_offer = -// static_cast(video_content->description); -// -// VideoContentDescription* video_accept = new VideoContentDescription(); -// -// for (VideoCodecs::const_iterator theirs = video_offer->codecs().begin(); -// theirs != video_offer->codecs().end(); ++theirs) { -// video_accept->AddCodec(*theirs); -// } -// video_accept->SortCodecs(); -// answer->AddContent(video_content->name, video_content->type, video_accept); -// } - - // process the remote candidates - remote_candidates_.clear(); - std::vector::iterator iter; - for (iter = candidates.begin(); iter != candidates.end(); ++iter) { - std::string tname = iter->name(); - TransportChannelMap::iterator titer = transport_channels_.find(tname); - if (titer != transport_channels_.end()) { - remote_candidates_.push_back(*iter); - titer->second->AddRemoteCandidate(*iter); - } - } - - set_remote_description(rdesc); - SetSessionState(STATE_RECEIVEDACCEPT); - return true; -} - -bool WebRTCSessionImpl::OnRemoteDescriptionUpdate( - const cricket::SessionDescription* desc, - std::vector& candidates) { - // This will be called when session is in connected state - // In this state session expects signaling message for any removed - // streamed by the peer. - // check for candidates port, if its equal to 0, remove that stream - // and provide callback OnRemoveStream else keep as it is - - for (size_t i = 0; i < candidates.size(); ++i) { - if (candidates[i].address().port() == 0) { - RemoveStreamOnRequest(candidates[i]); - } - } - return true; -} - -void WebRTCSessionImpl::RemoveStreamOnRequest(const cricket::Candidate& candidate) { - // 1. Get Transport corresponding to candidate name - // 2. Get StreamInfo for the transport found in step 1 - // 3. call ChannelManager Destroy voice/video method - - TransportChannelMap::iterator iter = - transport_channels_.find(candidate.name()); - if (iter == transport_channels_.end()) { - return; - } - - PC_Transport_Impl* transport = iter->second; - std::vector::iterator siter; - for (siter = streams_.begin(); siter != streams_.end(); ++siter) { - StreamInfo* stream_info = (*siter); - if (stream_info->transport == transport) { - if (!stream_info->video) { - cricket::VoiceChannel* channel = static_cast ( - stream_info->channel); - signaling_thread_->Post(this, MSG_RTC_DESTROYVOICECHANNEL, - new talk_base::TypedMessageData(channel)); - } else { - cricket::VideoChannel* channel = static_cast ( - stream_info->channel); - signaling_thread_->Post(this, MSG_RTC_DESTROYVIDEOCHANNEL, - new talk_base::TypedMessageData(channel)); - } - break; - } - } -} - -cricket::SessionDescription* WebRTCSessionImpl::CreateOffer() { - - SessionDescription* offer = new SessionDescription(); - StreamMap::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - if ((*iter)->video) { - // add video codecs, if there is video stream added - VideoContentDescription* video = new VideoContentDescription(); - std::vector video_codecs; - channel_manager_->GetSupportedVideoCodecs(&video_codecs); - for (VideoCodecs::const_iterator codec = video_codecs.begin(); - codec != video_codecs.end(); ++codec) { - video->AddCodec(*codec); - } - - video->SortCodecs(); - offer->AddContent(CN_VIDEO, NS_JINGLE_RTP, video); - } else { - AudioContentDescription* audio = new AudioContentDescription(); - - std::vector audio_codecs; - channel_manager_->GetSupportedAudioCodecs(&audio_codecs); - for (AudioCodecs::const_iterator codec = audio_codecs.begin(); - codec != audio_codecs.end(); ++codec) { - audio->AddCodec(*codec); - } - - audio->SortCodecs(); - offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio); - } - } - return offer; -} - -cricket::SessionDescription* WebRTCSessionImpl::CreateAnswer( - const cricket::SessionDescription* offer) { - cricket::SessionDescription* answer = new cricket::SessionDescription(); - - const ContentInfo* audio_content = GetFirstAudioContent(offer); - if (audio_content) { - const AudioContentDescription* audio_offer = - static_cast(audio_content->description); - - AudioContentDescription* audio_accept = new AudioContentDescription(); - AudioCodecs audio_codecs; - channel_manager_->GetSupportedAudioCodecs(&audio_codecs); - - for (AudioCodecs::const_iterator ours = audio_codecs.begin(); - ours != audio_codecs.end(); ++ours) { - for (AudioCodecs::const_iterator theirs = audio_offer->codecs().begin(); - theirs != audio_offer->codecs().end(); ++theirs) { - if (ours->Matches(*theirs)) { - cricket::AudioCodec negotiated(*ours); - negotiated.id = theirs->id; - audio_accept->AddCodec(negotiated); - } - } - } - audio_accept->SortCodecs(); - answer->AddContent(audio_content->name, audio_content->type, audio_accept); - } - - const ContentInfo* video_content = GetFirstVideoContent(offer); - if (video_content) { - const VideoContentDescription* video_offer = - static_cast(video_content->description); - - VideoContentDescription* video_accept = new VideoContentDescription(); - VideoCodecs video_codecs; - channel_manager_->GetSupportedVideoCodecs(&video_codecs); - - for (VideoCodecs::const_iterator ours = video_codecs.begin(); - ours != video_codecs.end(); ++ours) { - for (VideoCodecs::const_iterator theirs = video_offer->codecs().begin(); - theirs != video_offer->codecs().end(); ++theirs) { - if (ours->Matches(*theirs)) { - cricket::VideoCodec negotiated(*ours); - negotiated.id = theirs->id; - video_accept->AddCodec(negotiated); - } - } - } - video_accept->SortCodecs(); - answer->AddContent(video_content->name, video_content->type, video_accept); - } - return answer; -} - -void WebRTCSessionImpl::OnMute(bool mute) { -} - -void WebRTCSessionImpl::OnCameraMute(bool mute) { -} - -void WebRTCSessionImpl::SetError(Error error) { - if (signaling_thread_->IsCurrent()) { - BaseSession::SetError(error); - } else { - signaling_thread_->Post(this, MSG_RTC_SETSESSIONERROR, - new talk_base::TypedMessageData(error)); - } -} - -void WebRTCSessionImpl::OnCandidateReady(const cricket::Candidate& address) { - local_candidates_.push_back(address); - - // for now we are using only one candidate from each connection. - // PC_Transport_Impl will discard remaining candidates from - // P2PTransportChannel. When this function is called, if - // local_candidates_ size is equal to streams_ ( if RTCP is disabled) - // then send local session description - - // TODO(mallinath): Is it correct to check the state variable here for - // incoming sessions? - if ((signal_initiated_ || state_ == STATE_RECEIVEDINITIATE) && - (local_candidates_.size() == streams_.size())) { - SendLocalDescription(); - - // On the receiving end, we haven't yet enabled the channels, so after - // sending the local description, let's enable the channels. - if (!signal_initiated_) { - // Enable all the channels and then send our local description. - signaling_thread_->Post(this, MSG_RTC_ENABLEALLSTREAMS); - } - } -} - -void WebRTCSessionImpl::SendLocalDescription() { - signaling_thread_->Post(this, MSG_RTC_SENDLOCALDESCRIPTION); -} - -void WebRTCSessionImpl::SendLocalDescription_w() { - cricket::SessionDescription* desc; - if (incoming() && state_ == STATE_RECEIVEDINITIATE) { - desc = CreateAnswer(remote_description_); - } else { - desc = CreateOffer(); - } - if (desc) { - set_local_description(desc); - session_state_ = (incoming()) ? STATE_SENTACCEPT : STATE_SENTINITIATE; - SetState(session_state_); - connection()->OnLocalDescription(desc, local_candidates_); - } -} - -void WebRTCSessionImpl::SignalOnWritableState_w( - cricket::TransportChannel* channel) { - ASSERT(connection()->media_thread() == talk_base::Thread::Current()); - SignalWritableState(channel); -} - -void WebRTCSessionImpl::OnStateChange(P2PTransportClass::State state, - cricket::TransportChannel* channel) { - if (P2PTransportClass::STATE_WRITABLE & state) { - connection()->media_thread()->Post( - this, MSG_RTC_SIGNALONWRITABLESTATE, - new talk_base::TypedMessageData(channel)); - } -} - -void WebRTCSessionImpl::OnMessageReceived(const char* data, size_t data_size) { -} - -} /* namespace webrtc */ diff --git a/third_party_mods/libjingle/source/talk/app/webrtcsessionimpl_unittest.cc b/third_party_mods/libjingle/source/talk/app/webrtcsessionimpl_unittest.cc deleted file mode 100644 index 17e022dd7..000000000 --- a/third_party_mods/libjingle/source/talk/app/webrtcsessionimpl_unittest.cc +++ /dev/null @@ -1,100 +0,0 @@ -/* - * webrtcsessionimpl_unittest.cc - * - * Created on: Mar 11, 2011 - * Author: mallinath - */ - -#include "talk/base/gunit.h" -#include "talk/base/logging.h" -#include "talk/base/scoped_ptr.h" -#include "talk/base/sigslot.h" -#include "talk/app/webrtcsessionimpl.h" -#include "talk/p2p/client/basicportallocator.h" -#include "talk/session/phone/channelmanager.h" -#include "talk/session/phone/fakemediaengine.h" -#include "talk/session/phone/fakesession.h" - -namespace webrtc { -using talk_base::scoped_ptr; - -static const char* kTestSessionId = "1234"; - -class WebRTCSessionImplForTest : public WebRTCSessionImpl { - public: - WebRTCSessionImplForTest(const std::string& jid, const std::string& id, - const std::string& type, - const std::string& direction, - cricket::PortAllocator* allocator, - cricket::ChannelManager* channelmgr) - : WebRTCSessionImpl(NULL, id, type, direction, allocator, channelmgr) { - - } - - ~WebRTCSessionImplForTest() { - //Do Nothing - } - - virtual cricket::Transport* GetTransport() { - return static_cast(WebRTCSessionImpl::GetTransport()); - } - - protected: - virtual cricket::Transport* CreateTransport() { - return new cricket::FakeTransport(talk_base::Thread::Current(), talk_base::Thread::Current()); - } - -}; - -class WebRTCSessionImplTest : public sigslot::has_slots<>, - public testing::Test { - public: - WebRTCSessionImplTest() { - network_mgr_.reset(new talk_base::NetworkManager()); - port_allocator_.reset(new cricket::BasicPortAllocator(network_mgr_.get())); - media_engine_ = new cricket::FakeMediaEngine(); - channel_mgr_.reset(new cricket::ChannelManager(talk_base::Thread::Current())); - channel_mgr_.reset(NULL); - - } - ~WebRTCSessionImplTest() { - - } - - void CreateSession(const std::string& jid, const std::string& id, - const std::string& type, const std::string& dir) { - session_.reset(new WebRTCSessionImplForTest(jid, id, type, dir, - port_allocator_.get(), - channel_mgr_.get())); - } - bool InitiateCall(const std::string& jid, const std::string& id, - const std::string& type, const std::string& dir) { - CreateSession(jid, id, type, dir); - bool ret = session_->Initiate(); - return ret; - } - - bool GetCandidates() { - return InitiateCall("", kTestSessionId, "t", "s"); - - } - - - protected: - scoped_ptr network_mgr_; - scoped_ptr port_allocator_; - cricket::FakeMediaEngine* media_engine_; - scoped_ptr channel_mgr_; - scoped_ptr session_; - -}; - -TEST_F(WebRTCSessionImplTest, TestGetCandidatesCall) { - EXPECT_TRUE(GetCandidates()); - EXPECT_EQ(cricket::Session::STATE_INIT, session_->state()); - EXPECT_EQ(kTestSessionId, session_->id()); - EXPECT_EQ(WebRTCSession::kTestType, session_->type()); - EXPECT_FALSE(session_->incoming()); -} - -} /* namespace webrtc */ diff --git a/third_party_mods/libjingle/source/talk/base/json.cc b/third_party_mods/libjingle/source/talk/base/json.cc index 620c0e05d..6408403df 100644 --- a/third_party_mods/libjingle/source/talk/base/json.cc +++ b/third_party_mods/libjingle/source/talk/base/json.cc @@ -121,7 +121,7 @@ bool GetValueFromJsonArray(const Json::Value& in, size_t n, return false; } - *out = in[n]; + *out = in[static_cast(n)]; return true; } @@ -199,7 +199,7 @@ bool JsonValueToStringVector(const Json::Value& value, return false; } - for (size_t i = 0; i < value.size(); ++i) { + for (unsigned int i = 0; i < value.size(); ++i) { if (value[i].isString()) { strings->push_back(value[i].asString()); } else { diff --git a/third_party_mods/libjingle/source/talk/p2p/base/p2ptransportchannel.cc b/third_party_mods/libjingle/source/talk/p2p/base/p2ptransportchannel.cc deleted file mode 100644 index b4f540668..000000000 --- a/third_party_mods/libjingle/source/talk/p2p/base/p2ptransportchannel.cc +++ /dev/null @@ -1,972 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "talk/p2p/base/p2ptransportchannel.h" - -#include - -#include "talk/base/buffer.h" -#include "talk/base/common.h" -#include "talk/base/logging.h" -#include "talk/p2p/base/common.h" - -namespace { - -// messages for queuing up work for ourselves -const uint32 MSG_SORT = 1; -const uint32 MSG_PING = 2; -const uint32 MSG_ALLOCATE = 3; - -#ifdef PLATFORM_CHROMIUM -const uint32 MSG_SENDPACKET = 4; - -struct SendPacketParams : public talk_base::MessageData { - talk_base::Buffer packet; -}; -#endif - -// When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers) -// for pinging. When the socket is writable, we will use only 1 Kbps because -// we don't want to degrade the quality on a modem. These numbers should work -// well on a 28.8K modem, which is the slowest connection on which the voice -// quality is reasonable at all. -static const uint32 PING_PACKET_SIZE = 60 * 8; -static const uint32 WRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms -static const uint32 UNWRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 10000; // 50ms - -// If there is a current writable connection, then we will also try hard to -// make sure it is pinged at this rate. -static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; // 2*WRITABLE_DELAY - bit - -// The minimum improvement in RTT that justifies a switch. -static const double kMinImprovement = 10; - -// Amount of time that we wait when *losing* writability before we try doing -// another allocation. -static const int kAllocateDelay = 1 * 1000; // 1 second - -// We will try creating a new allocator from scratch after a delay of this -// length without becoming writable (or timing out). -static const int kAllocatePeriod = 20 * 1000; // 20 seconds - -cricket::Port::CandidateOrigin GetOrigin(cricket::Port* port, - cricket::Port* origin_port) { - if (!origin_port) - return cricket::Port::ORIGIN_MESSAGE; - else if (port == origin_port) - return cricket::Port::ORIGIN_THIS_PORT; - else - return cricket::Port::ORIGIN_OTHER_PORT; -} - -// Compares two connections based only on static information about them. -int CompareConnectionCandidates(cricket::Connection* a, - cricket::Connection* b) { - // Combine local and remote preferences - ASSERT(a->local_candidate().preference() == a->port()->preference()); - ASSERT(b->local_candidate().preference() == b->port()->preference()); - double a_pref = a->local_candidate().preference() - * a->remote_candidate().preference(); - double b_pref = b->local_candidate().preference() - * b->remote_candidate().preference(); - - // Now check combined preferences. Lower values get sorted last. - if (a_pref > b_pref) - return 1; - if (a_pref < b_pref) - return -1; - - // If we're still tied at this point, prefer a younger generation. - return (a->remote_candidate().generation() + a->port()->generation()) - - (b->remote_candidate().generation() + b->port()->generation()); -} - -// Compare two connections based on their writability and static preferences. -int CompareConnections(cricket::Connection *a, cricket::Connection *b) { - // Sort based on write-state. Better states have lower values. - if (a->write_state() < b->write_state()) - return 1; - if (a->write_state() > b->write_state()) - return -1; - - // Compare the candidate information. - return CompareConnectionCandidates(a, b); -} - -// Wraps the comparison connection into a less than operator that puts higher -// priority writable connections first. -class ConnectionCompare { - public: - bool operator()(const cricket::Connection *ca, - const cricket::Connection *cb) { - cricket::Connection* a = const_cast(ca); - cricket::Connection* b = const_cast(cb); - - // Compare first on writability and static preferences. - int cmp = CompareConnections(a, b); - if (cmp > 0) - return true; - if (cmp < 0) - return false; - - // Otherwise, sort based on latency estimate. - return a->rtt() < b->rtt(); - - // Should we bother checking for the last connection that last received - // data? It would help rendezvous on the connection that is also receiving - // packets. - // - // TODO: Yes we should definitely do this. The TCP protocol gains - // efficiency by being used bidirectionally, as opposed to two separate - // unidirectional streams. This test should probably occur before - // comparison of local prefs (assuming combined prefs are the same). We - // need to be careful though, not to bounce back and forth with both sides - // trying to rendevous with the other. - } -}; - -// Determines whether we should switch between two connections, based first on -// static preferences and then (if those are equal) on latency estimates. -bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) { - if (a_conn == b_conn) - return false; - - if (!a_conn || !b_conn) // don't think the latter should happen - return true; - - int prefs_cmp = CompareConnections(a_conn, b_conn); - if (prefs_cmp < 0) - return true; - if (prefs_cmp > 0) - return false; - - return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; -} - -} // unnamed namespace - -namespace cricket { - -P2PTransportChannel::P2PTransportChannel(const std::string &name, - const std::string &content_type, - P2PTransport* transport, - PortAllocator *allocator) : - TransportChannelImpl(name, content_type), - transport_(transport), - allocator_(allocator), - worker_thread_(talk_base::Thread::Current()), - incoming_only_(false), - waiting_for_signaling_(false), - error_(0), - best_connection_(NULL), - pinging_started_(false), - sort_dirty_(false), - was_writable_(false), - was_timed_out_(true) { -} - -P2PTransportChannel::~P2PTransportChannel() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - for (uint32 i = 0; i < allocator_sessions_.size(); ++i) - delete allocator_sessions_[i]; -} - -// Add the allocator session to our list so that we know which sessions -// are still active. -void P2PTransportChannel::AddAllocatorSession(PortAllocatorSession* session) { - session->set_generation(static_cast(allocator_sessions_.size())); - allocator_sessions_.push_back(session); - - // We now only want to apply new candidates that we receive to the ports - // created by this new session because these are replacing those of the - // previous sessions. - ports_.clear(); - - session->SignalPortReady.connect(this, &P2PTransportChannel::OnPortReady); - session->SignalCandidatesReady.connect( - this, &P2PTransportChannel::OnCandidatesReady); - session->GetInitialPorts(); - if (pinging_started_) - session->StartGetAllPorts(); -} - -// Go into the state of processing candidates, and running in general -void P2PTransportChannel::Connect() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Kick off an allocator session - Allocate(); - - // Start pinging as the ports come in. - thread()->Post(this, MSG_PING); -} - -// Reset the socket, clear up any previous allocations and start over -void P2PTransportChannel::Reset() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Get rid of all the old allocators. This should clean up everything. - for (uint32 i = 0; i < allocator_sessions_.size(); ++i) - delete allocator_sessions_[i]; - - allocator_sessions_.clear(); - ports_.clear(); - connections_.clear(); - best_connection_ = NULL; - - // Forget about all of the candidates we got before. - remote_candidates_.clear(); - - // Revert to the initial state. - set_readable(false); - set_writable(false); - - // Reinitialize the rest of our state. - waiting_for_signaling_ = false; - pinging_started_ = false; - sort_dirty_ = false; - was_writable_ = false; - was_timed_out_ = true; - - // If we allocated before, start a new one now. - if (transport_->connect_requested()) - Allocate(); - - // Start pinging as the ports come in. - thread()->Clear(this); - thread()->Post(this, MSG_PING); -} - -// A new port is available, attempt to make connections for it -void P2PTransportChannel::OnPortReady(PortAllocatorSession *session, - Port* port) { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Set in-effect options on the new port - for (OptionMap::const_iterator it = options_.begin(); - it != options_.end(); - ++it) { - int val = port->SetOption(it->first, it->second); - if (val < 0) { - LOG_J(LS_WARNING, port) << "SetOption(" << it->first - << ", " << it->second - << ") failed: " << port->GetError(); - } - } - - // Remember the ports and candidates, and signal that candidates are ready. - // The session will handle this, and send an initiate/accept/modify message - // if one is pending. - - ports_.push_back(port); - port->SignalUnknownAddress.connect( - this, &P2PTransportChannel::OnUnknownAddress); - port->SignalDestroyed.connect(this, &P2PTransportChannel::OnPortDestroyed); - - // Attempt to create a connection from this new port to all of the remote - // candidates that we were given so far. - - std::vector::iterator iter; - for (iter = remote_candidates_.begin(); iter != remote_candidates_.end(); - ++iter) { - CreateConnection(port, *iter, iter->origin_port(), false); - } - - SortConnections(); -} - -// A new candidate is available, let listeners know -void P2PTransportChannel::OnCandidatesReady( - PortAllocatorSession *session, const std::vector& candidates) { - for (size_t i = 0; i < candidates.size(); ++i) { - SignalCandidateReady(this, candidates[i]); - } -} - -// Handle stun packets -void P2PTransportChannel::OnUnknownAddress( - Port *port, const talk_base::SocketAddress &address, StunMessage *stun_msg, - const std::string &remote_username) { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Port has received a valid stun packet from an address that no Connection - // is currently available for. See if the remote user name is in the remote - // candidate list. If it isn't return error to the stun request. - - const Candidate *candidate = NULL; - std::vector::iterator it; - for (it = remote_candidates_.begin(); it != remote_candidates_.end(); ++it) { - if ((*it).username() == remote_username) { - candidate = &(*it); - break; - } - } - if (candidate == NULL) { - // Don't know about this username, the request is bogus - // This sometimes happens if a binding response comes in before the ACCEPT - // message. It is totally valid; the retry state machine will try again. - - port->SendBindingErrorResponse(stun_msg, address, - STUN_ERROR_STALE_CREDENTIALS, STUN_ERROR_REASON_STALE_CREDENTIALS); - delete stun_msg; - return; - } - - // Check for connectivity to this address. Create connections - // to this address across all local ports. First, add this as a new remote - // address - - Candidate new_remote_candidate = *candidate; - new_remote_candidate.set_address(address); - // new_remote_candidate.set_protocol(port->protocol()); - - // This remote username exists. Now create connections using this candidate, - // and resort - - if (CreateConnections(new_remote_candidate, port, true)) { - // Send the pinger a successful stun response. - port->SendBindingResponse(stun_msg, address); - - // Update the list of connections since we just added another. We do this - // after sending the response since it could (in principle) delete the - // connection in question. - SortConnections(); - } else { - // Hopefully this won't occur, because changing a destination address - // shouldn't cause a new connection to fail - ASSERT(false); - port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR, - STUN_ERROR_REASON_SERVER_ERROR); - } - - delete stun_msg; -} - -void P2PTransportChannel::OnCandidate(const Candidate& candidate) { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Create connections to this remote candidate. - CreateConnections(candidate, NULL, false); - - // Resort the connections list, which may have new elements. - SortConnections(); -} - -// Creates connections from all of the ports that we care about to the given -// remote candidate. The return value is true if we created a connection from -// the origin port. -bool P2PTransportChannel::CreateConnections(const Candidate &remote_candidate, - Port* origin_port, - bool readable) { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Add a new connection for this candidate to every port that allows such a - // connection (i.e., if they have compatible protocols) and that does not - // already have a connection to an equivalent candidate. We must be careful - // to make sure that the origin port is included, even if it was pruned, - // since that may be the only port that can create this connection. - - bool created = false; - - std::vector::reverse_iterator it; - for (it = ports_.rbegin(); it != ports_.rend(); ++it) { - if (CreateConnection(*it, remote_candidate, origin_port, readable)) { - if (*it == origin_port) - created = true; - } - } - - if ((origin_port != NULL) && - std::find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) { - if (CreateConnection(origin_port, remote_candidate, origin_port, readable)) - created = true; - } - - // Remember this remote candidate so that we can add it to future ports. - RememberRemoteCandidate(remote_candidate, origin_port); - - return created; -} - -// Setup a connection object for the local and remote candidate combination. -// And then listen to connection object for changes. -bool P2PTransportChannel::CreateConnection(Port* port, - const Candidate& remote_candidate, - Port* origin_port, - bool readable) { - // Look for an existing connection with this remote address. If one is not - // found, then we can create a new connection for this address. - Connection* connection = port->GetConnection(remote_candidate.address()); - if (connection != NULL) { - // It is not legal to try to change any of the parameters of an existing - // connection; however, the other side can send a duplicate candidate. - if (!remote_candidate.IsEquivalent(connection->remote_candidate())) { - LOG(INFO) << "Attempt to change a remote candidate"; - return false; - } - } else { - Port::CandidateOrigin origin = GetOrigin(port, origin_port); - - // Don't create connection if this is a candidate we received in a - // message and we are not allowed to make outgoing connections. - if (origin == cricket::Port::ORIGIN_MESSAGE && incoming_only_) - return false; - - connection = port->CreateConnection(remote_candidate, origin); - if (!connection) - return false; - - connections_.push_back(connection); - connection->SignalReadPacket.connect( - this, &P2PTransportChannel::OnReadPacket); - connection->SignalStateChange.connect( - this, &P2PTransportChannel::OnConnectionStateChange); - connection->SignalDestroyed.connect( - this, &P2PTransportChannel::OnConnectionDestroyed); - - LOG_J(LS_INFO, this) << "Created connection with origin=" << origin << ", (" - << connections_.size() << " total)"; - } - - // If we are readable, it is because we are creating this in response to a - // ping from the other side. This will cause the state to become readable. - if (readable) - connection->ReceivedPing(); - - return true; -} - -// Maintain our remote candidate list, adding this new remote one. -void P2PTransportChannel::RememberRemoteCandidate( - const Candidate& remote_candidate, Port* origin_port) { - // Remove any candidates whose generation is older than this one. The - // presence of a new generation indicates that the old ones are not useful. - uint32 i = 0; - while (i < remote_candidates_.size()) { - if (remote_candidates_[i].generation() < remote_candidate.generation()) { - LOG(INFO) << "Pruning candidate from old generation: " - << remote_candidates_[i].address().ToString(); - remote_candidates_.erase(remote_candidates_.begin() + i); - } else { - i += 1; - } - } - - // Make sure this candidate is not a duplicate. - for (uint32 i = 0; i < remote_candidates_.size(); ++i) { - if (remote_candidates_[i].IsEquivalent(remote_candidate)) { - LOG(INFO) << "Duplicate candidate: " - << remote_candidate.address().ToString(); - return; - } - } - - // Try this candidate for all future ports. - remote_candidates_.push_back(RemoteCandidate(remote_candidate, origin_port)); - - // We have some candidates from the other side, we are now serious about - // this connection. Let's do the StartGetAllPorts thing. - if (!pinging_started_) { - pinging_started_ = true; - for (size_t i = 0; i < allocator_sessions_.size(); ++i) { - if (!allocator_sessions_[i]->IsGettingAllPorts()) - allocator_sessions_[i]->StartGetAllPorts(); - } - } -} - -// Send data to the other side, using our best connection -int P2PTransportChannel::SendPacket(talk_base::Buffer* packet) { -#ifdef PLATFORM_CHROMIUM - if(worker_thread_ != talk_base::Thread::Current()) { - SendPacketParams* params = new SendPacketParams; - packet->TransferTo(¶ms->packet); - worker_thread_->Post(this, MSG_SENDPACKET, params); - return params->packet.length(); - } -#endif - - return SendPacket(packet->data(), packet->length()); -} - -// Send data to the other side, using our best connection -int P2PTransportChannel::SendPacket(const char *data, size_t len) { - // This can get called on any thread that is convenient to write from! - if (best_connection_ == NULL) { - error_ = EWOULDBLOCK; - return SOCKET_ERROR; - } - int sent = best_connection_->Send(data, len); - if (sent <= 0) { - ASSERT(sent < 0); - error_ = best_connection_->GetError(); - } - return sent; -} - -// Begin allocate (or immediately re-allocate, if MSG_ALLOCATE pending) -void P2PTransportChannel::Allocate() { - CancelPendingAllocate(); - // Time for a new allocator, lets make sure we have a signalling channel - // to communicate candidates through first. - waiting_for_signaling_ = true; - SignalRequestSignaling(); -} - -// Cancels the pending allocate, if any. -void P2PTransportChannel::CancelPendingAllocate() { - thread()->Clear(this, MSG_ALLOCATE); -} - -// Monitor connection states -void P2PTransportChannel::UpdateConnectionStates() { - uint32 now = talk_base::Time(); - - // We need to copy the list of connections since some may delete themselves - // when we call UpdateState. - for (uint32 i = 0; i < connections_.size(); ++i) - connections_[i]->UpdateState(now); -} - -// Prepare for best candidate sorting -void P2PTransportChannel::RequestSort() { - if (!sort_dirty_) { - worker_thread_->Post(this, MSG_SORT); - sort_dirty_ = true; - } -} - -// Sort the available connections to find the best one. We also monitor -// the number of available connections and the current state so that we -// can possibly kick off more allocators (for more connections). -void P2PTransportChannel::SortConnections() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Make sure the connection states are up-to-date since this affects how they - // will be sorted. - UpdateConnectionStates(); - - // Any changes after this point will require a re-sort. - sort_dirty_ = false; - - // Get a list of the networks that we are using. - std::set networks; - for (uint32 i = 0; i < connections_.size(); ++i) - networks.insert(connections_[i]->port()->network()); - - // Find the best alternative connection by sorting. It is important to note - // that amongst equal preference, writable connections, this will choose the - // one whose estimated latency is lowest. So it is the only one that we - // need to consider switching to. - - ConnectionCompare cmp; - std::stable_sort(connections_.begin(), connections_.end(), cmp); - LOG(LS_VERBOSE) << "Sorting available connections:"; - for (uint32 i = 0; i < connections_.size(); ++i) { - LOG(LS_VERBOSE) << connections_[i]->ToString(); - } - - Connection* top_connection = NULL; - if (connections_.size() > 0) - top_connection = connections_[0]; - - // If necessary, switch to the new choice. - if (ShouldSwitch(best_connection_, top_connection)) - SwitchBestConnectionTo(top_connection); - - // We can prune any connection for which there is a writable connection on - // the same network with better or equal prefences. We leave those with - // better preference just in case they become writable later (at which point, - // we would prune out the current best connection). We leave connections on - // other networks because they may not be using the same resources and they - // may represent very distinct paths over which we can switch. - std::set::iterator network; - for (network = networks.begin(); network != networks.end(); ++network) { - Connection* primier = GetBestConnectionOnNetwork(*network); - if (!primier || (primier->write_state() != Connection::STATE_WRITABLE)) - continue; - - for (uint32 i = 0; i < connections_.size(); ++i) { - if ((connections_[i] != primier) && - (connections_[i]->port()->network() == *network) && - (CompareConnectionCandidates(primier, connections_[i]) >= 0)) { - connections_[i]->Prune(); - } - } - } - - // Count the number of connections in the various states. - - int writable = 0; - int write_connect = 0; - int write_timeout = 0; - - for (uint32 i = 0; i < connections_.size(); ++i) { - switch (connections_[i]->write_state()) { - case Connection::STATE_WRITABLE: - ++writable; - break; - case Connection::STATE_WRITE_CONNECT: - ++write_connect; - break; - case Connection::STATE_WRITE_TIMEOUT: - ++write_timeout; - break; - default: - ASSERT(false); - } - } - - if (writable > 0) { - HandleWritable(); - } else if (write_connect > 0) { - HandleNotWritable(); - } else { - HandleAllTimedOut(); - } - - // Update the state of this channel. This method is called whenever the - // state of any connection changes, so this is a good place to do this. - UpdateChannelState(); - - // Notify of connection state change - SignalConnectionMonitor(this); -} - -// Track the best connection, and let listeners know -void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) { - // Note: if conn is NULL, the previous best_connection_ has been destroyed, - // so don't use it. - // use it. - Connection* old_best_connection = best_connection_; - best_connection_ = conn; - if (best_connection_) { - if (old_best_connection) { - LOG_J(LS_INFO, this) << "Previous best connection: " - << old_best_connection->ToString(); - } - LOG_J(LS_INFO, this) << "New best connection: " - << best_connection_->ToString(); - SignalRouteChange(this, best_connection_->remote_candidate()); - } else { - LOG_J(LS_INFO, this) << "No best connection"; - } -} - -void P2PTransportChannel::UpdateChannelState() { - // The Handle* functions already set the writable state. We'll just double- - // check it here. - bool writable = ((best_connection_ != NULL) && - (best_connection_->write_state() == - Connection::STATE_WRITABLE)); - ASSERT(writable == this->writable()); - if (writable != this->writable()) - LOG(LS_ERROR) << "UpdateChannelState: writable state mismatch"; - - bool readable = false; - for (uint32 i = 0; i < connections_.size(); ++i) { - if (connections_[i]->read_state() == Connection::STATE_READABLE) - readable = true; - } - set_readable(readable); -} - -// We checked the status of our connections and we had at least one that -// was writable, go into the writable state. -void P2PTransportChannel::HandleWritable() { - // - // One or more connections writable! - // - if (!writable()) { - for (uint32 i = 0; i < allocator_sessions_.size(); ++i) { - if (allocator_sessions_[i]->IsGettingAllPorts()) { - allocator_sessions_[i]->StopGetAllPorts(); - } - } - - // Stop further allocations. - CancelPendingAllocate(); - } - - // We're writable, obviously we aren't timed out - was_writable_ = true; - was_timed_out_ = false; - set_writable(true); -} - -// We checked the status of our connections and we didn't have any that -// were writable, go into the connecting state (kick off a new allocator -// session). -void P2PTransportChannel::HandleNotWritable() { - // - // No connections are writable but not timed out! - // - if (was_writable_) { - // If we were writable, let's kick off an allocator session immediately - was_writable_ = false; - Allocate(); - } - - // We were connecting, obviously not ALL timed out. - was_timed_out_ = false; - set_writable(false); -} - -// We checked the status of our connections and not only weren't they writable -// but they were also timed out, we really need a new allocator. -void P2PTransportChannel::HandleAllTimedOut() { - // - // No connections... all are timed out! - // - if (!was_timed_out_) { - // We weren't timed out before, so kick off an allocator now (we'll still - // be in the fully timed out state until the allocator actually gives back - // new ports) - Allocate(); - } - - // NOTE: we start was_timed_out_ in the true state so that we don't get - // another allocator created WHILE we are in the process of building up - // our first allocator. - was_timed_out_ = true; - was_writable_ = false; - set_writable(false); -} - -// If we have a best connection, return it, otherwise return top one in the -// list (later we will mark it best). -Connection* P2PTransportChannel::GetBestConnectionOnNetwork( - talk_base::Network* network) { - // If the best connection is on this network, then it wins. - if (best_connection_ && (best_connection_->port()->network() == network)) - return best_connection_; - - // Otherwise, we return the top-most in sorted order. - for (uint32 i = 0; i < connections_.size(); ++i) { - if (connections_[i]->port()->network() == network) - return connections_[i]; - } - - return NULL; -} - -// Handle any queued up requests -void P2PTransportChannel::OnMessage(talk_base::Message *pmsg) { - if (pmsg->message_id == MSG_SORT) - OnSort(); - else if (pmsg->message_id == MSG_PING) - OnPing(); - else if (pmsg->message_id == MSG_ALLOCATE) - Allocate(); -#ifdef PLATFORM_CHROMIUM - else if (pmsg->message_id == MSG_SENDPACKET) { - SendPacketParams* data = static_cast(pmsg->pdata); - SendPacket(&data->packet); - delete data; // because it is Posted - } -#endif - else - ASSERT(false); -} - -// Handle queued up sort request -void P2PTransportChannel::OnSort() { - // Resort the connections based on the new statistics. - SortConnections(); -} - -// Handle queued up ping request -void P2PTransportChannel::OnPing() { - // Make sure the states of the connections are up-to-date (since this affects - // which ones are pingable). - UpdateConnectionStates(); - - // Find the oldest pingable connection and have it do a ping. - Connection* conn = FindNextPingableConnection(); - if (conn) - conn->Ping(talk_base::Time()); - - // Post ourselves a message to perform the next ping. - uint32 delay = writable() ? WRITABLE_DELAY : UNWRITABLE_DELAY; - thread()->PostDelayed(delay, this, MSG_PING); -} - -// Is the connection in a state for us to even consider pinging the other side? -bool P2PTransportChannel::IsPingable(Connection* conn) { - // An unconnected connection cannot be written to at all, so pinging is out - // of the question. - if (!conn->connected()) - return false; - - if (writable()) { - // If we are writable, then we only want to ping connections that could be - // better than this one, i.e., the ones that were not pruned. - return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT); - } else { - // If we are not writable, then we need to try everything that might work. - // This includes both connections that do not have write timeout as well as - // ones that do not have read timeout. A connection could be readable but - // be in write-timeout if we pruned it before. Since the other side is - // still pinging it, it very well might still work. - return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT) || - (conn->read_state() != Connection::STATE_READ_TIMEOUT); - } -} - -// Returns the next pingable connection to ping. This will be the oldest -// pingable connection unless we have a writable connection that is past the -// maximum acceptable ping delay. -Connection* P2PTransportChannel::FindNextPingableConnection() { - uint32 now = talk_base::Time(); - if (best_connection_ && - (best_connection_->write_state() == Connection::STATE_WRITABLE) && - (best_connection_->last_ping_sent() - + MAX_CURRENT_WRITABLE_DELAY <= now)) { - return best_connection_; - } - - Connection* oldest_conn = NULL; - uint32 oldest_time = 0xFFFFFFFF; - for (uint32 i = 0; i < connections_.size(); ++i) { - if (IsPingable(connections_[i])) { - if (connections_[i]->last_ping_sent() < oldest_time) { - oldest_time = connections_[i]->last_ping_sent(); - oldest_conn = connections_[i]; - } - } - } - return oldest_conn; -} - -// return the number of "pingable" connections -uint32 P2PTransportChannel::NumPingableConnections() { - uint32 count = 0; - for (uint32 i = 0; i < connections_.size(); ++i) { - if (IsPingable(connections_[i])) - count += 1; - } - return count; -} - -// When a connection's state changes, we need to figure out who to use as -// the best connection again. It could have become usable, or become unusable. -void P2PTransportChannel::OnConnectionStateChange(Connection *connection) { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // We have to unroll the stack before doing this because we may be changing - // the state of connections while sorting. - RequestSort(); -} - -// When a connection is removed, edit it out, and then update our best -// connection. -void P2PTransportChannel::OnConnectionDestroyed(Connection *connection) { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Note: the previous best_connection_ may be destroyed by now, so don't - // use it. - - // Remove this connection from the list. - std::vector::iterator iter = - std::find(connections_.begin(), connections_.end(), connection); - ASSERT(iter != connections_.end()); - connections_.erase(iter); - - LOG_J(LS_INFO, this) << "Removed connection (" - << static_cast(connections_.size()) << " remaining)"; - - // If this is currently the best connection, then we need to pick a new one. - // The call to SortConnections will pick a new one. It looks at the current - // best connection in order to avoid switching between fairly similar ones. - // Since this connection is no longer an option, we can just set best to NULL - // and re-choose a best assuming that there was no best connection. - if (best_connection_ == connection) { - SwitchBestConnectionTo(NULL); - RequestSort(); - } -} - -// When a port is destroyed remove it from our list of ports to use for -// connection attempts. -void P2PTransportChannel::OnPortDestroyed(Port* port) { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Remove this port from the list (if we didn't drop it already). - std::vector::iterator iter = - std::find(ports_.begin(), ports_.end(), port); - if (iter != ports_.end()) - ports_.erase(iter); - - LOG(INFO) << "Removed port from p2p socket: " - << static_cast(ports_.size()) << " remaining"; -} - -// We data is available, let listeners know -void P2PTransportChannel::OnReadPacket(Connection *connection, - const char *data, size_t len) { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - - // Let the client know of an incoming packet - - SignalReadPacket(this, data, len); -} - -// Set options on ourselves is simply setting options on all of our available -// port objects. -int P2PTransportChannel::SetOption(talk_base::Socket::Option opt, int value) { - OptionMap::iterator it = options_.find(opt); - if (it == options_.end()) { - options_.insert(std::make_pair(opt, value)); - } else if (it->second == value) { - return 0; - } else { - it->second = value; - } - - for (uint32 i = 0; i < ports_.size(); ++i) { - int val = ports_[i]->SetOption(opt, value); - if (val < 0) { - // Because this also occurs deferred, probably no point in reporting an - // error - LOG(WARNING) << "SetOption(" << opt << ", " << value << ") failed: " - << ports_[i]->GetError(); - } - } - return 0; -} - -// When the signalling channel is ready, we can really kick off the allocator -void P2PTransportChannel::OnSignalingReady() { - if (waiting_for_signaling_) { - waiting_for_signaling_ = false; - AddAllocatorSession(allocator_->CreateSession(name(), content_type())); - thread()->PostDelayed(kAllocatePeriod, this, MSG_ALLOCATE); - } -} - -} // namespace cricket diff --git a/third_party_mods/libjingle/source/talk/p2p/base/p2ptransportchannel.h b/third_party_mods/libjingle/source/talk/p2p/base/p2ptransportchannel.h deleted file mode 100644 index 0288697f0..000000000 --- a/third_party_mods/libjingle/source/talk/p2p/base/p2ptransportchannel.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// P2PTransportChannel wraps up the state management of the connection between -// two P2P clients. Clients have candidate ports for connecting, and -// connections which are combinations of candidates from each end (Alice and -// Bob each have candidates, one candidate from Alice and one candidate from -// Bob are used to make a connection, repeat to make many connections). -// -// When all of the available connections become invalid (non-writable), we -// kick off a process of determining more candidates and more connections. -// -#ifndef TALK_P2P_BASE_P2PTRANSPORTCHANNEL_H_ -#define TALK_P2P_BASE_P2PTRANSPORTCHANNEL_H_ - -#include -#include -#include - -#include "talk/base/sigslot.h" -#include "talk/p2p/base/candidate.h" -#include "talk/p2p/base/port.h" -#include "talk/p2p/base/portallocator.h" -#include "talk/p2p/base/transport.h" -#include "talk/p2p/base/transportchannelimpl.h" -#include "talk/p2p/base/p2ptransport.h" - -namespace cricket { - -// Adds the port on which the candidate originated. -class RemoteCandidate : public Candidate { - public: - RemoteCandidate(const Candidate& c, Port* origin_port) - : Candidate(c), origin_port_(origin_port) {} - - Port* origin_port() { return origin_port_; } - - private: - Port* origin_port_; -}; - -// P2PTransportChannel manages the candidates and connection process to keep -// two P2P clients connected to each other. -class P2PTransportChannel : public TransportChannelImpl, - public talk_base::MessageHandler { - public: - P2PTransportChannel(const std::string &name, - const std::string &content_type, - P2PTransport* transport, - PortAllocator *allocator); - virtual ~P2PTransportChannel(); - - // From TransportChannelImpl: - virtual Transport* GetTransport() { return transport_; } - virtual void Connect(); - virtual void Reset(); - virtual void OnSignalingReady(); - - // From TransportChannel: - virtual int SendPacket(talk_base::Buffer* packet); - virtual int SendPacket(const char *data, size_t len); - virtual int SetOption(talk_base::Socket::Option opt, int value); - virtual int GetError() { return error_; } - - // This hack is here to allow the SocketMonitor to downcast to the - // P2PTransportChannel safely. - virtual P2PTransportChannel* GetP2PChannel() { return this; } - - // These are used by the connection monitor. - sigslot::signal1 SignalConnectionMonitor; - const std::vector& connections() const { return connections_; } - Connection* best_connection() const { return best_connection_; } - - void set_incoming_only(bool value) { incoming_only_ = value; } - - // Handler for internal messages. - virtual void OnMessage(talk_base::Message *pmsg); - - virtual void OnCandidate(const Candidate& candidate); - - private: - void Allocate(); - void CancelPendingAllocate(); - void UpdateConnectionStates(); - void RequestSort(); - void SortConnections(); - void SwitchBestConnectionTo(Connection* conn); - void UpdateChannelState(); - void HandleWritable(); - void HandleNotWritable(); - void HandleAllTimedOut(); - Connection* GetBestConnectionOnNetwork(talk_base::Network* network); - bool CreateConnections(const Candidate &remote_candidate, Port* origin_port, - bool readable); - bool CreateConnection(Port* port, const Candidate& remote_candidate, - Port* origin_port, bool readable); - void RememberRemoteCandidate(const Candidate& remote_candidate, - Port* origin_port); - void OnUnknownAddress(Port *port, const talk_base::SocketAddress &addr, - StunMessage *stun_msg, - const std::string &remote_username); - void OnPortReady(PortAllocatorSession *session, Port* port); - void OnCandidatesReady(PortAllocatorSession *session, - const std::vector& candidates); - void OnConnectionStateChange(Connection *connection); - void OnConnectionDestroyed(Connection *connection); - void OnPortDestroyed(Port* port); - void OnReadPacket(Connection *connection, const char *data, size_t len); - void OnSort(); - void OnPing(); - bool IsPingable(Connection* conn); - Connection* FindNextPingableConnection(); - uint32 NumPingableConnections(); - PortAllocatorSession* allocator_session() { - return allocator_sessions_.back(); - } - void AddAllocatorSession(PortAllocatorSession* session); - - talk_base::Thread* thread() const { return worker_thread_; } - - P2PTransport* transport_; - PortAllocator *allocator_; - talk_base::Thread *worker_thread_; - bool incoming_only_; - bool waiting_for_signaling_; - int error_; - std::vector allocator_sessions_; - std::vector ports_; - std::vector connections_; - Connection *best_connection_; - std::vector remote_candidates_; - // indicates whether StartGetAllCandidates has been called - bool pinging_started_; - bool sort_dirty_; // indicates whether another sort is needed right now - bool was_writable_; - bool was_timed_out_; - typedef std::map OptionMap; - OptionMap options_; - - DISALLOW_EVIL_CONSTRUCTORS(P2PTransportChannel); -}; - -} // namespace cricket - -#endif // TALK_P2P_BASE_P2PTRANSPORTCHANNEL_H_ diff --git a/third_party_mods/libjingle/source/talk/p2p/base/session.h b/third_party_mods/libjingle/source/talk/p2p/base/session.h deleted file mode 100644 index b46f438fd..000000000 --- a/third_party_mods/libjingle/source/talk/p2p/base/session.h +++ /dev/null @@ -1,546 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_P2P_BASE_SESSION_H_ -#define TALK_P2P_BASE_SESSION_H_ - -#include -#include -#include -#include - -#include "talk/p2p/base/sessionmessages.h" -#include "talk/p2p/base/sessionmanager.h" -#include "talk/base/socketaddress.h" -#include "talk/p2p/base/sessionclient.h" -#include "talk/p2p/base/parsing.h" -#include "talk/p2p/base/port.h" -#include "talk/xmllite/xmlelement.h" -#include "talk/xmpp/constants.h" - -namespace cricket { - -class P2PTransportChannel; -class Transport; -class TransportChannel; -class TransportChannelProxy; -class TransportChannelImpl; - -// Used for errors that will send back a specific error message to the -// remote peer. We add "type" to the errors because it's needed for -// SignalErrorMessage. -struct MessageError : ParseError { - buzz::QName type; - - // if unset, assume type is a parse error - MessageError() : ParseError(), type(buzz::QN_STANZA_BAD_REQUEST) {} - - void SetType(const buzz::QName type) { - this->type = type; - } -}; - -// Used for errors that may be returned by public session methods that -// can fail. -// TODO: Use this error in Session::Initiate and -// Session::Accept. -struct SessionError : WriteError { -}; - -// Bundles a Transport and ChannelMap together. ChannelMap is used to -// create transport channels before receiving or sending a session -// initiate, and for speculatively connecting channels. Previously, a -// session had one ChannelMap and transport. Now, with multiple -// transports per session, we need multiple ChannelMaps as well. -class TransportProxy { - public: - TransportProxy(const std::string& content_name, Transport* transport) - : content_name_(content_name), - transport_(transport), - state_(STATE_INIT), - sent_candidates_(false) {} - ~TransportProxy(); - - std::string content_name() const { return content_name_; } - Transport* impl() const { return transport_; } - std::string type() const; - bool negotiated() const { return state_ == STATE_NEGOTIATED; } - const Candidates& sent_candidates() const { return sent_candidates_; } - - TransportChannel* GetChannel(const std::string& name); - TransportChannel* CreateChannel(const std::string& name, - const std::string& content_type); - void DestroyChannel(const std::string& name); - void AddSentCandidates(const Candidates& candidates); - void ClearSentCandidates() { sent_candidates_.clear(); } - void SpeculativelyConnectChannels(); - void CompleteNegotiation(); - - private: - enum TransportState { - STATE_INIT, - STATE_CONNECTING, - STATE_NEGOTIATED - }; - - typedef std::map ChannelMap; - - TransportChannelProxy* GetProxy(const std::string& name); - TransportChannelImpl* GetOrCreateImpl(const std::string& name, - const std::string& content_type); - void SetProxyImpl(const std::string& name, TransportChannelProxy* proxy); - - std::string content_name_; - Transport* transport_; - TransportState state_; - ChannelMap channels_; - Candidates sent_candidates_; -}; - -typedef std::map TransportMap; - -// TODO: Consider simplifying the dependency from Voice/VideoChannel -// on Session. Right now the Channel class requires a BaseSession, but it only -// uses CreateChannel/DestroyChannel. Perhaps something like a -// TransportChannelFactory could be hoisted up out of BaseSession, or maybe -// the transports could be passed in directly. - -// A BaseSession manages general session state. This includes negotiation -// of both the application-level and network-level protocols: the former -// defines what will be sent and the latter defines how it will be sent. Each -// network-level protocol is represented by a Transport object. Each Transport -// participates in the network-level negotiation. The individual streams of -// packets are represented by TransportChannels. The application-level protocol -// is represented by SessionDecription objects. -class BaseSession : public sigslot::has_slots<>, - public talk_base::MessageHandler { - public: - enum State { - STATE_INIT = 0, - STATE_SENTINITIATE, // sent initiate, waiting for Accept or Reject - STATE_RECEIVEDINITIATE, // received an initiate. Call Accept or Reject - STATE_SENTACCEPT, // sent accept. begin connecting transport - STATE_RECEIVEDACCEPT, // received accept. begin connecting transport - STATE_SENTMODIFY, // sent modify, waiting for Accept or Reject - STATE_RECEIVEDMODIFY, // received modify, call Accept or Reject - STATE_SENTREJECT, // sent reject after receiving initiate - STATE_RECEIVEDREJECT, // received reject after sending initiate - STATE_SENTREDIRECT, // sent direct after receiving initiate - STATE_SENTTERMINATE, // sent terminate (any time / either side) - STATE_RECEIVEDTERMINATE, // received terminate (any time / either side) - STATE_INPROGRESS, // session accepted and in progress - STATE_DEINIT, // session is being destroyed - }; - - enum Error { - ERROR_NONE = 0, // no error - ERROR_TIME = 1, // no response to signaling - ERROR_RESPONSE = 2, // error during signaling - ERROR_NETWORK = 3, // network error, could not allocate network resources - ERROR_CONTENT = 4, // channel errors in SetLocalContent/SetRemoteContent - }; - - explicit BaseSession(talk_base::Thread *signaling_thread); - virtual ~BaseSession(); - - // Updates the state, signaling if necessary. - void SetState(State state); - - // Updates the error state, signaling if necessary. - virtual void SetError(Error error); - - // Handles messages posted to us. - virtual void OnMessage(talk_base::Message *pmsg); - - // Returns the current state of the session. See the enum above for details. - // Each time the state changes, we will fire this signal. - State state() const { return state_; } - sigslot::signal2 SignalState; - - // Returns the last error in the session. See the enum above for details. - // Each time the an error occurs, we will fire this signal. - Error error() const { return error_; } - sigslot::signal2 SignalError; - - sigslot::signal1 SignalWritableState; - sigslot::signal3 SignalReadPacket; - - - // Creates a new channel with the given names. This method may be called - // immediately after creating the session. However, the actual - // implementation may not be fixed until transport negotiation completes. - // This will usually be called from the worker thread, but that - // shouldn't be an issue since the main thread will be blocked in - // Send when doing so. - virtual TransportChannel* CreateChannel(const std::string& content_name, - const std::string& channel_name) = 0; - - // Returns the channel with the given names. - virtual TransportChannel* GetChannel(const std::string& content_name, - const std::string& channel_name) = 0; - - // Destroys the channel with the given names. - // This will usually be called from the worker thread, but that - // shouldn't be an issue since the main thread will be blocked in - // Send when doing so. - virtual void DestroyChannel(const std::string& content_name, - const std::string& channel_name) = 0; - - // Invoked when we notice that there is no matching channel on our peer. - sigslot::signal2 SignalChannelGone; - - // Returns the application-level description given by our client. - // If we are the recipient, this will be NULL until we send an accept. - const SessionDescription* local_description() const { - return local_description_; - } - // Takes ownership of SessionDescription* - bool set_local_description(const SessionDescription* sdesc) { - if (sdesc != local_description_) { - delete local_description_; - local_description_ = sdesc; - } - return true; - } - - // Returns the application-level description given by the other client. - // If we are the initiator, this will be NULL until we receive an accept. - const SessionDescription* remote_description() const { - return remote_description_; - } - // Takes ownership of SessionDescription* - bool set_remote_description(const SessionDescription* sdesc) { - if (sdesc != remote_description_) { - delete remote_description_; - remote_description_ = sdesc; - } - return true; - } - - // When we receive an initiate, we create a session in the - // RECEIVEDINITIATE state and respond by accepting or rejecting. - // Takes ownership of session description. - virtual bool Accept(const SessionDescription* sdesc) = 0; - virtual bool Reject(const std::string& reason) = 0; - bool Terminate() { - return TerminateWithReason(STR_TERMINATE_SUCCESS); - } - virtual bool TerminateWithReason(const std::string& reason) = 0; - - // The worker thread used by the session manager - virtual talk_base::Thread *worker_thread() = 0; - - talk_base::Thread *signaling_thread() { - return signaling_thread_; - } - - // Returns the JID of this client. - const std::string& local_name() const { return local_name_; } - - // Returns the JID of the other peer in this session. - const std::string& remote_name() const { return remote_name_; } - - // Set the JID of the other peer in this session. - // Typically the remote_name_ is set when the session is initiated. - // However, sometimes (e.g when a proxy is used) the peer name is - // known after the BaseSession has been initiated and it must be updated - // explicitly. - void set_remote_name(const std::string& name) { remote_name_ = name; } - - const std::string& id() const { return sid_; } - - protected: - State state_; - Error error_; - const SessionDescription* local_description_; - const SessionDescription* remote_description_; - std::string sid_; - // We don't use buzz::Jid because changing to buzz:Jid here has a - // cascading effect that requires an enormous number places to - // change to buzz::Jid as well. - std::string local_name_; - std::string remote_name_; - talk_base::Thread *signaling_thread_; -}; - -// A specific Session created by the SessionManager, using XMPP for protocol. -class Session : public BaseSession { - public: - // Returns the manager that created and owns this session. - SessionManager* session_manager() const { return session_manager_; } - - // the worker thread used by the session manager - talk_base::Thread *worker_thread() { - return session_manager_->worker_thread(); - } - - // Returns the XML namespace identifying the type of this session. - const std::string& content_type() const { return content_type_; } - - // Returns the client that is handling the application data of this session. - SessionClient* client() const { return client_; } - - SignalingProtocol current_protocol() const { return current_protocol_; } - - void set_current_protocol(SignalingProtocol protocol) { - current_protocol_ = protocol; - } - - // Indicates whether we initiated this session. - bool initiator() const { return initiator_; } - - const SessionDescription* initiator_description() const { - if (initiator_) { - return local_description_; - } else { - return remote_description_; - } - } - - // Fired whenever we receive a terminate message along with a reason - sigslot::signal2 SignalReceivedTerminateReason; - - void set_allow_local_ips(bool allow); - - // Returns the transport that has been negotiated or NULL if - // negotiation is still in progress. - Transport* GetTransport(const std::string& content_name); - - // Takes ownership of session description. - // TODO: Add an error argument to pass back to the caller. - bool Initiate(const std::string& to, - const SessionDescription* sdesc); - - // When we receive an initiate, we create a session in the - // RECEIVEDINITIATE state and respond by accepting or rejecting. - // Takes ownership of session description. - // TODO: Add an error argument to pass back to the caller. - virtual bool Accept(const SessionDescription* sdesc); - virtual bool Reject(const std::string& reason); - virtual bool TerminateWithReason(const std::string& reason); - - // The two clients in the session may also send one another - // arbitrary XML messages, which are called "info" messages. Sending - // takes ownership of the given elements. The signal does not; the - // parent element will be deleted after the signal. - bool SendInfoMessage(const XmlElements& elems); - sigslot::signal2 SignalInfoMessage; - - // Maps passed to serialization functions. - TransportParserMap GetTransportParsers(); - ContentParserMap GetContentParsers(); - - // Creates a new channel with the given names. This method may be called - // immediately after creating the session. However, the actual - // implementation may not be fixed until transport negotiation completes. - virtual TransportChannel* CreateChannel(const std::string& content_name, - const std::string& channel_name); - - // Returns the channel with the given names. - virtual TransportChannel* GetChannel(const std::string& content_name, - const std::string& channel_name); - - // Destroys the channel with the given names. - virtual void DestroyChannel(const std::string& content_name, - const std::string& channel_name); - - // Updates the error state, signaling if necessary. - virtual void SetError(Error error); - - // Handles messages posted to us. - virtual void OnMessage(talk_base::Message *pmsg); - - private: - // Creates or destroys a session. (These are called only SessionManager.) - Session(SessionManager *session_manager, - const std::string& local_name, const std::string& initiator_name, - const std::string& sid, const std::string& content_type, - SessionClient* client); - ~Session(); - - // Get a TransportProxy by content_name or transport. NULL if not found. - TransportProxy* GetTransportProxy(const std::string& content_name); - TransportProxy* GetTransportProxy(const Transport* transport); - TransportProxy* GetFirstTransportProxy(); - // TransportProxy is owned by session. Return proxy just for convenience. - TransportProxy* GetOrCreateTransportProxy(const std::string& content_name); - // For each transport info, create a transport proxy. Can fail for - // incompatible transport types. - bool CreateTransportProxies(const TransportInfos& tinfos, - SessionError* error); - void SpeculativelyConnectAllTransportChannels(); - bool OnRemoteCandidates(const TransportInfos& tinfos, - ParseError* error); - // Returns a TransportInfo without candidates for each content name. - // Uses the transport_type_ of the session. - TransportInfos GetEmptyTransportInfos(const ContentInfos& contents) const; - - // Called when the first channel of a transport begins connecting. We use - // this to start a timer, to make sure that the connection completes in a - // reasonable amount of time. - void OnTransportConnecting(Transport* transport); - - // Called when a transport changes its writable state. We track this to make - // sure that the transport becomes writable within a reasonable amount of - // time. If this does not occur, we signal an error. - void OnTransportWritable(Transport* transport); - - // Called when a transport requests signaling. - void OnTransportRequestSignaling(Transport* transport); - - // Called when a transport signals that it has a message to send. Note that - // these messages are just the transport part of the stanza; they need to be - // wrapped in the appropriate session tags. - void OnTransportCandidatesReady(Transport* transport, - const Candidates& candidates); - - // Called when a transport signals that it found an error in an incoming - // message. - void OnTransportSendError(Transport* transport, - const buzz::XmlElement* stanza, - const buzz::QName& name, - const std::string& type, - const std::string& text, - const buzz::XmlElement* extra_info); - - // Called when we notice that one of our local channels has no peer, so it - // should be destroyed. - void OnTransportChannelGone(Transport* transport, const std::string& name); - - // When the session needs to send signaling messages, it beings by requesting - // signaling. The client should handle this by calling OnSignalingReady once - // it is ready to send the messages. - // (These are called only by SessionManager.) - sigslot::signal1 SignalRequestSignaling; - void OnSignalingReady(); - - // Send various kinds of session messages. - bool SendInitiateMessage(const SessionDescription* sdesc, - SessionError* error); - bool SendAcceptMessage(const SessionDescription* sdesc, SessionError* error); - bool SendRejectMessage(const std::string& reason, SessionError* error); - bool SendTerminateMessage(const std::string& reason, SessionError* error); - bool SendTransportInfoMessage(const TransportInfo& tinfo, - SessionError* error); - bool ResendAllTransportInfoMessages(SessionError* error); - - // Both versions of SendMessage send a message of the given type to - // the other client. Can pass either a set of elements or an - // "action", which must have a WriteSessionAction method to go along - // with it. Sending with an action supports sending a "hybrid" - // message. Sending with elements must be sent as Jingle or Gingle. - - // When passing elems, must be either Jingle or Gingle protocol. - // Takes ownership of action_elems. - bool SendMessage(ActionType type, const XmlElements& action_elems, - SessionError* error); - // When passing an action, may be Hybrid protocol. - template - bool SendMessage(ActionType type, const Action& action, - SessionError* error); - - // Helper methods to write the session message stanza. - template - bool WriteActionMessage(ActionType type, const Action& action, - buzz::XmlElement* stanza, WriteError* error); - template - bool WriteActionMessage(SignalingProtocol protocol, - ActionType type, const Action& action, - buzz::XmlElement* stanza, WriteError* error); - - // Sending messages in hybrid form requires being able to write them - // on a per-protocol basis with a common method signature, which all - // of these have. - bool WriteSessionAction(SignalingProtocol protocol, - const SessionInitiate& init, - XmlElements* elems, WriteError* error); - bool WriteSessionAction(SignalingProtocol protocol, - const TransportInfo& tinfo, - XmlElements* elems, WriteError* error); - bool WriteSessionAction(SignalingProtocol protocol, - const SessionTerminate& term, - XmlElements* elems, WriteError* error); - - // Sends a message back to the other client indicating that we have received - // and accepted their message. - void SendAcknowledgementMessage(const buzz::XmlElement* stanza); - - // Once signaling is ready, the session will use this signal to request the - // sending of each message. When messages are received by the other client, - // they should be handed to OnIncomingMessage. - // (These are called only by SessionManager.) - sigslot::signal2 SignalOutgoingMessage; - void OnIncomingMessage(const SessionMessage& msg); - - void OnFailedSend(const buzz::XmlElement* orig_stanza, - const buzz::XmlElement* error_stanza); - - // Invoked when an error is found in an incoming message. This is translated - // into the appropriate XMPP response by SessionManager. - sigslot::signal6 SignalErrorMessage; - - // Handlers for the various types of messages. These functions may take - // pointers to the whole stanza or to just the session element. - bool OnInitiateMessage(const SessionMessage& msg, MessageError* error); - bool OnAcceptMessage(const SessionMessage& msg, MessageError* error); - bool OnRejectMessage(const SessionMessage& msg, MessageError* error); - bool OnInfoMessage(const SessionMessage& msg); - bool OnTerminateMessage(const SessionMessage& msg, MessageError* error); - bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error); - bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error); - bool OnUpdateMessage(const SessionMessage& msg, MessageError* error); - bool OnRedirectError(const SessionRedirect& redirect, SessionError* error); - - // Verifies that we are in the appropriate state to receive this message. - bool CheckState(State state, MessageError* error); - - SessionManager *session_manager_; - bool initiator_; - std::string initiator_name_; - std::string content_type_; - SessionClient* client_; - std::string transport_type_; - TransportParser* transport_parser_; - // This is transport-specific but required so much by unit tests - // that it's much easier to put it here. - bool allow_local_ips_; - TransportMap transports_; - // Keeps track of what protocol we are speaking. - SignalingProtocol current_protocol_; - - friend class SessionManager; // For access to constructor, destructor, - // and signaling related methods. -}; - -} // namespace cricket - -#endif // TALK_P2P_BASE_SESSION_H_ diff --git a/third_party_mods/libjingle/source/talk/p2p/base/transportchannel.h b/third_party_mods/libjingle/source/talk/p2p/base/transportchannel.h deleted file mode 100644 index 45de275c4..000000000 --- a/third_party_mods/libjingle/source/talk/p2p/base/transportchannel.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_P2P_BASE_TRANSPORTCHANNEL_H_ -#define TALK_P2P_BASE_TRANSPORTCHANNEL_H_ - -#include -#include "talk/base/basictypes.h" -#include "talk/base/sigslot.h" -#include "talk/base/socket.h" - -namespace talk_base { -class Buffer; -} - -namespace cricket { - -class Candidate; -class P2PTransportChannel; - -// A TransportChannel represents one logical stream of packets that are sent -// between the two sides of a session. -class TransportChannel: public sigslot::has_slots<> { - public: - TransportChannel(const std::string& name, const std::string &content_type) - : name_(name), content_type_(content_type), - readable_(false), writable_(false) {} - virtual ~TransportChannel() {} - - // Returns the name of this channel. - const std::string& name() const { return name_; } - const std::string& content_type() const { return content_type_; } - - // Returns the readable and states of this channel. Each time one of these - // states changes, a signal is raised. These states are aggregated by the - // TransportManager. - bool readable() const { return readable_; } - bool writable() const { return writable_; } - sigslot::signal1 SignalReadableState; - sigslot::signal1 SignalWritableState; - - virtual int SendPacket(talk_base::Buffer* packet) = 0; - // Attempts to send the given packet. The return value is < 0 on failure. - virtual int SendPacket(const char *data, size_t len) = 0; - - // Sets a socket option on this channel. Note that not all options are - // supported by all transport types. - virtual int SetOption(talk_base::Socket::Option opt, int value) = 0; - - // Returns the most recent error that occurred on this channel. - virtual int GetError() = 0; - - // This hack is here to allow the SocketMonitor to downcast to the - // P2PTransportChannel safely. - // TODO: Generalize network monitoring. - virtual P2PTransportChannel* GetP2PChannel() { return NULL; } - - // Signalled each time a packet is received on this channel. - sigslot::signal3 SignalReadPacket; - - // This signal occurs when there is a change in the way that packets are - // being routed, i.e. to a different remote location. The candidate - // indicates where and how we are currently sending media. - sigslot::signal2 SignalRouteChange; - - // Invoked when the channel is being destroyed. - sigslot::signal1 SignalDestroyed; - - // Debugging description of this transport channel. - std::string ToString() const; - - protected: - // Sets the readable state, signaling if necessary. - void set_readable(bool readable); - - // Sets the writable state, signaling if necessary. - void set_writable(bool writable); - - private: - std::string name_; - std::string content_type_; - bool readable_; - bool writable_; - - DISALLOW_EVIL_CONSTRUCTORS(TransportChannel); -}; - -} // namespace cricket - -#endif // TALK_P2P_BASE_TRANSPORTCHANNEL_H_ diff --git a/third_party_mods/libjingle/source/talk/p2p/base/transportchannelproxy.cc b/third_party_mods/libjingle/source/talk/p2p/base/transportchannelproxy.cc deleted file mode 100644 index 96fd56371..000000000 --- a/third_party_mods/libjingle/source/talk/p2p/base/transportchannelproxy.cc +++ /dev/null @@ -1,112 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "talk/p2p/base/transportchannelproxy.h" -#include "talk/base/common.h" -#include "talk/p2p/base/transport.h" -#include "talk/p2p/base/transportchannelimpl.h" - -namespace cricket { - -TransportChannelProxy::TransportChannelProxy(const std::string& name, - const std::string& content_type) - : TransportChannel(name, content_type), impl_(NULL) { -} - -TransportChannelProxy::~TransportChannelProxy() { - if (impl_) - impl_->GetTransport()->DestroyChannel(impl_->name()); -} - -void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) { - impl_ = impl; - impl_->SignalReadableState.connect( - this, &TransportChannelProxy::OnReadableState); - impl_->SignalWritableState.connect( - this, &TransportChannelProxy::OnWritableState); - impl_->SignalReadPacket.connect(this, &TransportChannelProxy::OnReadPacket); - impl_->SignalRouteChange.connect(this, &TransportChannelProxy::OnRouteChange); - for (OptionList::iterator it = pending_options_.begin(); - it != pending_options_.end(); - ++it) { - impl_->SetOption(it->first, it->second); - } - pending_options_.clear(); -} - -int TransportChannelProxy::SendPacket(talk_base::Buffer* packet) { - // Fail if we don't have an impl yet. - return (impl_) ? impl_->SendPacket(packet) : -1; -} - -int TransportChannelProxy::SendPacket(const char *data, size_t len) { - // Fail if we don't have an impl yet. - return (impl_) ? impl_->SendPacket(data, len) : -1; -} - -int TransportChannelProxy::SetOption(talk_base::Socket::Option opt, int value) { - if (impl_) - return impl_->SetOption(opt, value); - pending_options_.push_back(OptionPair(opt, value)); - return 0; -} - -int TransportChannelProxy::GetError() { - ASSERT(impl_ != NULL); // should not be used until channel is writable - return impl_->GetError(); -} - -P2PTransportChannel* TransportChannelProxy::GetP2PChannel() { - if (impl_) { - return impl_->GetP2PChannel(); - } - return NULL; -} - -void TransportChannelProxy::OnReadableState(TransportChannel* channel) { - ASSERT(channel == impl_); - set_readable(impl_->readable()); -} - -void TransportChannelProxy::OnWritableState(TransportChannel* channel) { - ASSERT(channel == impl_); - set_writable(impl_->writable()); -} - -void TransportChannelProxy::OnReadPacket(TransportChannel* channel, - const char* data, size_t size) { - ASSERT(channel == impl_); - SignalReadPacket(this, data, size); -} - -void TransportChannelProxy::OnRouteChange(TransportChannel* channel, - const Candidate& candidate) { - ASSERT(channel == impl_); - SignalRouteChange(this, candidate); -} - -} // namespace cricket diff --git a/third_party_mods/libjingle/source/talk/p2p/base/transportchannelproxy.h b/third_party_mods/libjingle/source/talk/p2p/base/transportchannelproxy.h deleted file mode 100644 index aa9dffc02..000000000 --- a/third_party_mods/libjingle/source/talk/p2p/base/transportchannelproxy.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_P2P_BASE_TRANSPORTCHANNELPROXY_H_ -#define TALK_P2P_BASE_TRANSPORTCHANNELPROXY_H_ - -#include -#include -#include "talk/p2p/base/transportchannel.h" - -namespace talk_base { -class Buffer; -} - -namespace cricket { - -class TransportChannelImpl; - -// Proxies calls between the client and the transport channel implementation. -// This is needed because clients are allowed to create channels before the -// network negotiation is complete. Hence, we create a proxy up front, and -// when negotiation completes, connect the proxy to the implementaiton. -class TransportChannelProxy: public TransportChannel { - public: - TransportChannelProxy(const std::string& name, - const std::string& content_type); - virtual ~TransportChannelProxy(); - - TransportChannelImpl* impl() { return impl_; } - - // Sets the implementation to which we will proxy. - void SetImplementation(TransportChannelImpl* impl); - - // Implementation of the TransportChannel interface. These simply forward to - // the implementation. - virtual int SendPacket(talk_base::Buffer* packet); - virtual int SendPacket(const char *data, size_t len); - virtual int SetOption(talk_base::Socket::Option opt, int value); - virtual int GetError(); - virtual P2PTransportChannel* GetP2PChannel(); - - private: - typedef std::pair OptionPair; - typedef std::vector OptionList; - TransportChannelImpl* impl_; - OptionList pending_options_; - - // Catch signals from the implementation channel. These just forward to the - // client (after updating our state to match). - void OnReadableState(TransportChannel* channel); - void OnWritableState(TransportChannel* channel); - void OnReadPacket(TransportChannel* channel, const char* data, size_t size); - void OnRouteChange(TransportChannel* channel, const Candidate& candidate); - - DISALLOW_EVIL_CONSTRUCTORS(TransportChannelProxy); -}; - -} // namespace cricket - -#endif // TALK_P2P_BASE_TRANSPORTCHANNELPROXY_H_ diff --git a/third_party_mods/libjingle/source/talk/session/phone/channel.cc b/third_party_mods/libjingle/source/talk/session/phone/channel.cc deleted file mode 100644 index 7d27b0cf2..000000000 --- a/third_party_mods/libjingle/source/talk/session/phone/channel.cc +++ /dev/null @@ -1,1259 +0,0 @@ -/* - * libjingle - * Copyright 2004--2007, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "talk/session/phone/channel.h" - -#include "talk/base/buffer.h" -#include "talk/base/byteorder.h" -#include "talk/base/common.h" -#include "talk/base/logging.h" -#include "talk/p2p/base/transportchannel.h" -#include "talk/session/phone/channelmanager.h" -#include "talk/session/phone/mediasessionclient.h" -#include "talk/session/phone/mediasink.h" -#include "talk/session/phone/rtcpmuxfilter.h" -#include "talk/session/phone/rtputils.h" - -namespace cricket { - -struct PacketMessageData : public talk_base::MessageData { - talk_base::Buffer packet; -}; - -struct VoiceChannelErrorMessageData : public talk_base::MessageData { - VoiceChannelErrorMessageData(uint32 in_ssrc, - VoiceMediaChannel::Error in_error) - : ssrc(in_ssrc), - error(in_error) {} - uint32 ssrc; - VoiceMediaChannel::Error error; -}; - -struct VideoChannelErrorMessageData : public talk_base::MessageData { - VideoChannelErrorMessageData(uint32 in_ssrc, - VideoMediaChannel::Error in_error) - : ssrc(in_ssrc), - error(in_error) {} - uint32 ssrc; - VideoMediaChannel::Error error; -}; - -static const char* PacketType(bool rtcp) { - return (!rtcp) ? "RTP" : "RTCP"; -} - -static bool ValidPacket(bool rtcp, const talk_base::Buffer* packet) { - // Check the packet size. We could check the header too if needed. - return (packet && - packet->length() >= (!rtcp ? kMinRtpPacketLen : kMinRtcpPacketLen) && - packet->length() <= kMaxRtpPacketLen); -} - -BaseChannel::BaseChannel(talk_base::Thread* thread, MediaEngine* media_engine, - MediaChannel* media_channel, BaseSession* session, - const std::string& content_name, - TransportChannel* transport_channel) - : worker_thread_(thread), - media_engine_(media_engine), - session_(session), - media_channel_(media_channel), - received_media_sink_(NULL), - sent_media_sink_(NULL), - content_name_(content_name), - transport_channel_(transport_channel), - rtcp_transport_channel_(NULL), - enabled_(false), - writable_(false), - has_codec_(false), - muted_(false) { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - media_channel_->SetInterface(this); - -#ifdef PLATFORM_CHROMIUM - session_->SignalWritableState.connect( - this, &BaseChannel::OnWritableState); - session_->SignalReadPacket.connect( - this, &BaseChannel::OnChannelRead); -#else - transport_channel_->SignalWritableState.connect( - this, &BaseChannel::OnWritableState); - transport_channel_->SignalReadPacket.connect( - this, &BaseChannel::OnChannelRead); -#endif - - - LOG(LS_INFO) << "Created channel"; - - session->SignalState.connect(this, &BaseChannel::OnSessionState); -} - -BaseChannel::~BaseChannel() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - StopConnectionMonitor(); - FlushRtcpMessages(); // Send any outstanding RTCP packets. - Clear(); // eats any outstanding messages or packets - // We must destroy the media channel before the transport channel, otherwise - // the media channel may try to send on the dead transport channel. NULLing - // is not an effective strategy since the sends will come on another thread. - delete media_channel_; - set_rtcp_transport_channel(NULL); - if (transport_channel_ != NULL) - session_->DestroyChannel(content_name_, transport_channel_->name()); - LOG(LS_INFO) << "Destroyed channel"; -} - -bool BaseChannel::Enable(bool enable) { - // Can be called from thread other than worker thread - Send(enable ? MSG_ENABLE : MSG_DISABLE); - return true; -} - -bool BaseChannel::Mute(bool mute) { - // Can be called from thread other than worker thread - Send(mute ? MSG_MUTE : MSG_UNMUTE); - return true; -} - -bool BaseChannel::RemoveStream(uint32 ssrc) { - StreamMessageData data(ssrc, 0); - Send(MSG_REMOVESTREAM, &data); - return true; -} - -bool BaseChannel::SetRtcpCName(const std::string& cname) { - SetRtcpCNameData data(cname); - Send(MSG_SETRTCPCNAME, &data); - return data.result; -} - -bool BaseChannel::SetLocalContent(const MediaContentDescription* content, - ContentAction action) { - SetContentData data(content, action); - Send(MSG_SETLOCALCONTENT, &data); - return data.result; -} - -bool BaseChannel::SetRemoteContent(const MediaContentDescription* content, - ContentAction action) { - SetContentData data(content, action); - Send(MSG_SETREMOTECONTENT, &data); - return data.result; -} - -bool BaseChannel::SetMaxSendBandwidth(int max_bandwidth) { - SetBandwidthData data(max_bandwidth); - Send(MSG_SETMAXSENDBANDWIDTH, &data); - return data.result; -} - -void BaseChannel::StartConnectionMonitor(int cms) { - socket_monitor_.reset(new SocketMonitor(transport_channel_, - worker_thread(), - talk_base::Thread::Current())); - socket_monitor_->SignalUpdate.connect( - this, &BaseChannel::OnConnectionMonitorUpdate); - socket_monitor_->Start(cms); -} - -void BaseChannel::StopConnectionMonitor() { - if (socket_monitor_.get()) { - socket_monitor_->Stop(); - socket_monitor_.reset(); - } -} - -void BaseChannel::set_rtcp_transport_channel(TransportChannel* channel) { - if (rtcp_transport_channel_ != channel) { - if (rtcp_transport_channel_) { - session_->DestroyChannel(content_name_, rtcp_transport_channel_->name()); - } - rtcp_transport_channel_ = channel; - if (rtcp_transport_channel_) { - rtcp_transport_channel_->SignalWritableState.connect( - this, &BaseChannel::OnWritableState); - rtcp_transport_channel_->SignalReadPacket.connect( - this, &BaseChannel::OnChannelRead); - } - } -} - -bool BaseChannel::SendPacket(talk_base::Buffer* packet) { - return SendPacket(false, packet); -} - -bool BaseChannel::SendRtcp(talk_base::Buffer* packet) { - return SendPacket(true, packet); -} - -int BaseChannel::SetOption(SocketType type, talk_base::Socket::Option opt, - int value) { - switch (type) { - case ST_RTP: return transport_channel_->SetOption(opt, value); - case ST_RTCP: return rtcp_transport_channel_->SetOption(opt, value); - default: return -1; - } -} - -void BaseChannel::OnWritableState(TransportChannel* channel) { -#ifdef PLATFORM_CHROMIUM - // since session is issuing signal, there can be multiple channels - // for a session - if (channel != transport_channel_ && channel != rtcp_transport_channel_) { - return; - } -#else - ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_); -#endif - if (transport_channel_->writable() - && (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) { - ChannelWritable_w(); - } else { - ChannelNotWritable_w(); - } -} - -void BaseChannel::OnChannelRead(TransportChannel* channel, - const char* data, size_t len) { - // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine - ASSERT(worker_thread_ == talk_base::Thread::Current()); -#ifdef PLATFORM_CHROMIUM - if (channel != transport_channel_ && channel != rtcp_transport_channel_) { - return; - } -#endif - - // When using RTCP multiplexing we might get RTCP packets on the RTP - // transport. We feed RTP traffic into the demuxer to determine if it is RTCP. - bool rtcp = PacketIsRtcp(channel, data, len); - talk_base::Buffer packet(data, len); - HandlePacket(rtcp, &packet); -} - -bool BaseChannel::PacketIsRtcp(const TransportChannel* channel, - const char* data, size_t len) { - return (channel == rtcp_transport_channel_ || - rtcp_mux_filter_.DemuxRtcp(data, len)); -} - -bool BaseChannel::SendPacket(bool rtcp, talk_base::Buffer* packet) { - // SendPacket gets called from MediaEngine, typically on an encoder thread. - // If the thread is not our worker thread, we will post to our worker - // so that the real work happens on our worker. This avoids us having to - // synchronize access to all the pieces of the send path, including - // SRTP and the inner workings of the transport channels. - // The only downside is that we can't return a proper failure code if - // needed. Since UDP is unreliable anyway, this should be a non-issue. - if (talk_base::Thread::Current() != worker_thread_) { - // Avoid a copy by transferring the ownership of the packet data. - int message_id = (!rtcp) ? MSG_RTPPACKET : MSG_RTCPPACKET; - PacketMessageData* data = new PacketMessageData; - packet->TransferTo(&data->packet); - worker_thread_->Post(this, message_id, data); - return true; - } - - // Make sure we have a place to send this packet before doing anything. - // (We might get RTCP packets that we don't intend to send.) - // If we've negotiated RTCP mux, send RTCP over the RTP transport. - TransportChannel* channel = (!rtcp || rtcp_mux_filter_.IsActive()) ? - transport_channel_ : rtcp_transport_channel_; - if (!channel) { - return false; - } - - // Protect ourselves against crazy data. - if (!ValidPacket(rtcp, packet)) { - LOG(LS_ERROR) << "Dropping outgoing " << content_name_ << " " - << PacketType(rtcp) << " packet: wrong size=" - << packet->length(); - return false; - } - - // Push the packet down to the media sink. - // Need to do this before protecting the packet. - { - talk_base::CritScope cs(&sink_critical_section_); - if (sent_media_sink_) { - if (!rtcp) { - sent_media_sink_->OnRtpPacket(packet->data(), packet->length()); - } else { - sent_media_sink_->OnRtcpPacket(packet->data(), packet->length()); - } - } - } - - // Protect if needed. - if (srtp_filter_.IsActive()) { - bool res; - char* data = packet->data(); - int len = packet->length(); - if (!rtcp) { - res = srtp_filter_.ProtectRtp(data, len, packet->capacity(), &len); - if (!res) { - int seq_num = -1; - uint32 ssrc = 0; - GetRtpSeqNum(data, len, &seq_num); - GetRtpSsrc(data, len, &ssrc); - LOG(LS_ERROR) << "Failed to protect " << content_name_ - << " RTP packet: size=" << len - << ", seqnum=" << seq_num << ", SSRC=" << ssrc; - return false; - } - } else { - res = srtp_filter_.ProtectRtcp(data, len, packet->capacity(), &len); - if (!res) { - int type = -1; - GetRtcpType(data, len, &type); - LOG(LS_ERROR) << "Failed to protect " << content_name_ - << " RTCP packet: size=" << len << ", type=" << type; - return false; - } - } - - // Update the length of the packet now that we've added the auth tag. - packet->SetLength(len); - } - - // Bon voyage. - return (channel->SendPacket(packet) - == static_cast(packet->length())); -} - -void BaseChannel::HandlePacket(bool rtcp, talk_base::Buffer* packet) { - // Protect ourselvs against crazy data. - if (!ValidPacket(rtcp, packet)) { - LOG(LS_ERROR) << "Dropping incoming " << content_name_ << " " - << PacketType(rtcp) << " packet: wrong size=" - << packet->length(); - return; - } - - // Unprotect the packet, if needed. - if (srtp_filter_.IsActive()) { - char* data = packet->data(); - int len = packet->length(); - bool res; - if (!rtcp) { - res = srtp_filter_.UnprotectRtp(data, len, &len); - if (!res) { - int seq_num = -1; - uint32 ssrc = 0; - GetRtpSeqNum(data, len, &seq_num); - GetRtpSsrc(data, len, &ssrc); - LOG(LS_ERROR) << "Failed to unprotect " << content_name_ - << " RTP packet: size=" << len - << ", seqnum=" << seq_num << ", SSRC=" << ssrc; - return; - } - } else { - res = srtp_filter_.UnprotectRtcp(data, len, &len); - if (!res) { - int type = -1; - GetRtcpType(data, len, &type); - LOG(LS_ERROR) << "Failed to unprotect " << content_name_ - << " RTCP packet: size=" << len << ", type=" << type; - return; - } - } - - packet->SetLength(len); - } - - // Push it down to the media channel. - if (!rtcp) { - media_channel_->OnPacketReceived(packet); - } else { - media_channel_->OnRtcpReceived(packet); - } - - // Push it down to the media sink. - { - talk_base::CritScope cs(&sink_critical_section_); - if (received_media_sink_) { - if (!rtcp) { - received_media_sink_->OnRtpPacket(packet->data(), packet->length()); - } else { - received_media_sink_->OnRtcpPacket(packet->data(), packet->length()); - } - } - } -} - -void BaseChannel::OnSessionState(BaseSession* session, - BaseSession::State state) { - const MediaContentDescription* content = NULL; - switch (state) { - case Session::STATE_SENTINITIATE: - content = GetFirstContent(session->local_description()); - if (content && !SetLocalContent(content, CA_OFFER)) { - LOG(LS_ERROR) << "Failure in SetLocalContent with CA_OFFER"; - session->SetError(BaseSession::ERROR_CONTENT); - } - break; - case Session::STATE_SENTACCEPT: - content = GetFirstContent(session->local_description()); - if (content && !SetLocalContent(content, CA_ANSWER)) { - LOG(LS_ERROR) << "Failure in SetLocalContent with CA_ANSWER"; - session->SetError(BaseSession::ERROR_CONTENT); - } - break; - case Session::STATE_RECEIVEDINITIATE: - content = GetFirstContent(session->remote_description()); - if (content && !SetRemoteContent(content, CA_OFFER)) { - LOG(LS_ERROR) << "Failure in SetRemoteContent with CA_OFFER"; - session->SetError(BaseSession::ERROR_CONTENT); - } - break; - case Session::STATE_RECEIVEDACCEPT: - content = GetFirstContent(session->remote_description()); - if (content && !SetRemoteContent(content, CA_ANSWER)) { - LOG(LS_ERROR) << "Failure in SetRemoteContent with CA_ANSWER"; - session->SetError(BaseSession::ERROR_CONTENT); - } - break; - default: - break; - } -} - -void BaseChannel::EnableMedia_w() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - if (enabled_) - return; - - LOG(LS_INFO) << "Channel enabled"; - enabled_ = true; - ChangeState(); -} - -void BaseChannel::DisableMedia_w() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - if (!enabled_) - return; - - LOG(LS_INFO) << "Channel disabled"; - enabled_ = false; - ChangeState(); -} - -void BaseChannel::MuteMedia_w() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - if (muted_) - return; - - if (media_channel()->Mute(true)) { - LOG(LS_INFO) << "Channel muted"; - muted_ = true; - } -} - -void BaseChannel::UnmuteMedia_w() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - if (!muted_) - return; - - if (media_channel()->Mute(false)) { - LOG(LS_INFO) << "Channel unmuted"; - muted_ = false; - } -} - -void BaseChannel::ChannelWritable_w() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - if (writable_) - return; - LOG(LS_INFO) << "Channel socket writable (" - << transport_channel_->name().c_str() << ")"; - writable_ = true; - ChangeState(); -} - -void BaseChannel::ChannelNotWritable_w() { - ASSERT(worker_thread_ == talk_base::Thread::Current()); - if (!writable_) - return; - - LOG(LS_INFO) << "Channel socket not writable (" - << transport_channel_->name().c_str() << ")"; - writable_ = false; - ChangeState(); -} - -// Sets the maximum video bandwidth for automatic bandwidth adjustment. -bool BaseChannel::SetMaxSendBandwidth_w(int max_bandwidth) { - return media_channel()->SetSendBandwidth(true, max_bandwidth); -} - -bool BaseChannel::SetRtcpCName_w(const std::string& cname) { - return media_channel()->SetRtcpCName(cname); -} - -bool BaseChannel::SetSrtp_w(const std::vector& cryptos, - ContentAction action, ContentSource src) { - bool ret; - if (action == CA_OFFER) { - ret = srtp_filter_.SetOffer(cryptos, src); - } else if (action == CA_ANSWER) { - ret = srtp_filter_.SetAnswer(cryptos, src); - } else { - // CA_UPDATE, no crypto params. - ret = true; - } - return ret; -} - -bool BaseChannel::SetRtcpMux_w(bool enable, ContentAction action, - ContentSource src) { - bool ret; - if (action == CA_OFFER) { - ret = rtcp_mux_filter_.SetOffer(enable, src); - } else if (action == CA_ANSWER) { - ret = rtcp_mux_filter_.SetAnswer(enable, src); - if (ret && rtcp_mux_filter_.IsActive()) { - // We activated RTCP mux, close down the RTCP transport. - set_rtcp_transport_channel(NULL); - // If the RTP transport is already writable, then so are we. - if (transport_channel_->writable()) { - ChannelWritable_w(); - } - } - } else { - // CA_UPDATE, no RTCP mux info. - ret = true; - } - return ret; -} - -void BaseChannel::OnMessage(talk_base::Message *pmsg) { - switch (pmsg->message_id) { - case MSG_ENABLE: - EnableMedia_w(); - break; - case MSG_DISABLE: - DisableMedia_w(); - break; - - case MSG_MUTE: - MuteMedia_w(); - break; - case MSG_UNMUTE: - UnmuteMedia_w(); - break; - - case MSG_SETRTCPCNAME: { - SetRtcpCNameData* data = static_cast(pmsg->pdata); - data->result = SetRtcpCName_w(data->cname); - break; - } - - case MSG_SETLOCALCONTENT: { - SetContentData* data = static_cast(pmsg->pdata); - data->result = SetLocalContent_w(data->content, data->action); - break; - } - case MSG_SETREMOTECONTENT: { - SetContentData* data = static_cast(pmsg->pdata); - data->result = SetRemoteContent_w(data->content, data->action); - break; - } - - case MSG_REMOVESTREAM: { - StreamMessageData* data = static_cast(pmsg->pdata); - RemoveStream_w(data->ssrc1); - break; - } - - case MSG_SETMAXSENDBANDWIDTH: { - SetBandwidthData* data = static_cast(pmsg->pdata); - data->result = SetMaxSendBandwidth_w(data->value); - break; - } - - case MSG_RTPPACKET: - case MSG_RTCPPACKET: { - PacketMessageData* data = static_cast(pmsg->pdata); - SendPacket(pmsg->message_id == MSG_RTCPPACKET, &data->packet); - delete data; // because it is Posted - break; - } - } -} - -void BaseChannel::Send(uint32 id, talk_base::MessageData *pdata) { - worker_thread_->Send(this, id, pdata); -} - -void BaseChannel::Post(uint32 id, talk_base::MessageData *pdata) { - worker_thread_->Post(this, id, pdata); -} - -void BaseChannel::PostDelayed(int cmsDelay, uint32 id, - talk_base::MessageData *pdata) { - worker_thread_->PostDelayed(cmsDelay, this, id, pdata); -} - -void BaseChannel::Clear(uint32 id, talk_base::MessageList* removed) { - worker_thread_->Clear(this, id, removed); -} - -void BaseChannel::FlushRtcpMessages() { - // Flush all remaining RTCP messages. This should only be called in - // destructor. - ASSERT(talk_base::Thread::Current() == worker_thread_); - talk_base::MessageList rtcp_messages; - Clear(MSG_RTCPPACKET, &rtcp_messages); - for (talk_base::MessageList::iterator it = rtcp_messages.begin(); - it != rtcp_messages.end(); ++it) { - Send(MSG_RTCPPACKET, it->pdata); - } -} - -VoiceChannel::VoiceChannel(talk_base::Thread* thread, - MediaEngine* media_engine, - VoiceMediaChannel* media_channel, - BaseSession* session, - const std::string& content_name, - bool rtcp) - : BaseChannel(thread, media_engine, media_channel, session, content_name, - session->CreateChannel(content_name, "rtp")), - received_media_(false) { - if (rtcp) { - set_rtcp_transport_channel(session->CreateChannel(content_name, "rtcp")); - } - // Can't go in BaseChannel because certain session states will - // trigger pure virtual functions, such as GetFirstContent(). - OnSessionState(session, session->state()); - - media_channel->SignalMediaError.connect( - this, &VoiceChannel::OnVoiceChannelError); - srtp_filter()->SignalSrtpError.connect( - this, &VoiceChannel::OnSrtpError); -} - -VoiceChannel::~VoiceChannel() { - StopAudioMonitor(); - StopMediaMonitor(); - // this can't be done in the base class, since it calls a virtual - DisableMedia_w(); -} - -bool VoiceChannel::AddStream(uint32 ssrc) { - StreamMessageData data(ssrc, 0); - Send(MSG_ADDSTREAM, &data); - return true; -} - -bool VoiceChannel::SetRingbackTone(const void* buf, int len) { - SetRingbackToneMessageData data(buf, len); - Send(MSG_SETRINGBACKTONE, &data); - return data.result; -} - -// TODO: Handle early media the right way. We should get an explicit -// ringing message telling us to start playing local ringback, which we cancel -// if any early media actually arrives. For now, we do the opposite, which is -// to wait 1 second for early media, and start playing local ringback if none -// arrives. -void VoiceChannel::SetEarlyMedia(bool enable) { - if (enable) { - // Start the early media timeout - PostDelayed(kEarlyMediaTimeout, MSG_EARLYMEDIATIMEOUT); - } else { - // Stop the timeout if currently going. - Clear(MSG_EARLYMEDIATIMEOUT); - } -} - -bool VoiceChannel::PlayRingbackTone(uint32 ssrc, bool play, bool loop) { - PlayRingbackToneMessageData data(ssrc, play, loop); - Send(MSG_PLAYRINGBACKTONE, &data); - return data.result; -} - -bool VoiceChannel::PressDTMF(int digit, bool playout) { - DtmfMessageData data(digit, playout); - Send(MSG_PRESSDTMF, &data); - return data.result; -} - -void VoiceChannel::StartMediaMonitor(int cms) { - media_monitor_.reset(new VoiceMediaMonitor(media_channel(), worker_thread(), - talk_base::Thread::Current())); - media_monitor_->SignalUpdate.connect( - this, &VoiceChannel::OnMediaMonitorUpdate); - media_monitor_->Start(cms); -} - -void VoiceChannel::StopMediaMonitor() { - if (media_monitor_.get()) { - media_monitor_->Stop(); - media_monitor_->SignalUpdate.disconnect(this); - media_monitor_.reset(); - } -} - -void VoiceChannel::StartAudioMonitor(int cms) { - audio_monitor_.reset(new AudioMonitor(this, talk_base::Thread::Current())); - audio_monitor_ - ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate); - audio_monitor_->Start(cms); -} - -void VoiceChannel::StopAudioMonitor() { - if (audio_monitor_.get()) { - audio_monitor_->Stop(); - audio_monitor_.reset(); - } -} - -int VoiceChannel::GetInputLevel_w() { - return media_engine()->GetInputLevel(); -} - -int VoiceChannel::GetOutputLevel_w() { - return media_channel()->GetOutputLevel(); -} - -void VoiceChannel::GetActiveStreams_w(AudioInfo::StreamList* actives) { - media_channel()->GetActiveStreams(actives); -} - -void VoiceChannel::OnChannelRead(TransportChannel* channel, - const char* data, size_t len) { - BaseChannel::OnChannelRead(channel, data, len); - - // Set a flag when we've received an RTP packet. If we're waiting for early - // media, this will disable the timeout. - if (!received_media_ && !PacketIsRtcp(channel, data, len)) { - received_media_ = true; - } -} - -void VoiceChannel::ChangeState() { - // render incoming data if we are the active call - // we receive data on the default channel and multiplexed streams - bool recv = enabled(); - if (!media_channel()->SetPlayout(recv)) { - SendLastMediaError(); - } - - // send outgoing data if we are the active call, have the - // remote party's codec, and have a writable transport - // we only send data on the default channel - bool send = enabled() && has_codec() && writable(); - SendFlags send_flag = send ? SEND_MICROPHONE : SEND_NOTHING; - if (!media_channel()->SetSend(send_flag)) { - LOG(LS_ERROR) << "Failed to SetSend " << send_flag << " on voice channel"; - SendLastMediaError(); - } - - LOG(LS_INFO) << "Changing voice state, recv=" << recv << " send=" << send; -} - -const MediaContentDescription* VoiceChannel::GetFirstContent( - const SessionDescription* sdesc) { - const ContentInfo* cinfo = GetFirstAudioContent(sdesc); - if (cinfo == NULL) - return NULL; - - return static_cast(cinfo->description); -} - -bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, - ContentAction action) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - LOG(LS_INFO) << "Setting local voice description"; - - const AudioContentDescription* audio = - static_cast(content); - ASSERT(audio != NULL); - - bool ret; - if (audio->ssrc_set()) { - media_channel()->SetSendSsrc(audio->ssrc()); - LOG(LS_INFO) << "Set send ssrc for audio: " << audio->ssrc(); - } - // set SRTP - ret = SetSrtp_w(audio->cryptos(), action, CS_LOCAL); - - // set RTCP mux - if (ret) - ret = SetRtcpMux_w(audio->rtcp_mux(), action, CS_LOCAL); - - // set payload type and config for voice codecs - if (ret) - ret = media_channel()->SetRecvCodecs(audio->codecs()); - - // set header extensions - if (ret && audio->rtp_header_extensions_set()) { - ret = media_channel()->SetRecvRtpHeaderExtensions( - audio->rtp_header_extensions()); - } - - return ret; -} - -bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - LOG(LS_INFO) << "Setting remote voice description"; - - const AudioContentDescription* audio = - static_cast(content); - ASSERT(audio != NULL); - - // set SRTP - bool ret = SetSrtp_w(audio->cryptos(), action, CS_REMOTE); - // set RTCP mux - if (ret) { - ret = SetRtcpMux_w(audio->rtcp_mux(), action, CS_REMOTE); - } - // set codecs and payload types - if (ret) { - ret = media_channel()->SetSendCodecs(audio->codecs()); - } - // set header extensions - if (ret && audio->rtp_header_extensions_set()) { - ret = media_channel()->SetSendRtpHeaderExtensions( - audio->rtp_header_extensions()); - } - - int audio_options = 0; - if (audio->conference_mode()) { - audio_options |= OPT_CONFERENCE; - } - if (!media_channel()->SetOptions(audio_options)) { - // Log an error on failure, but don't abort the call. - LOG(LS_ERROR) << "Failed to set voice channel options"; - } - - // update state - if (ret) { - set_has_codec(true); - ChangeState(); - } - return ret; -} - -void VoiceChannel::AddStream_w(uint32 ssrc) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - media_channel()->AddStream(ssrc); -} - -void VoiceChannel::RemoveStream_w(uint32 ssrc) { - media_channel()->RemoveStream(ssrc); -} - -bool VoiceChannel::SetRingbackTone_w(const void* buf, int len) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - return media_channel()->SetRingbackTone(static_cast(buf), len); -} - -bool VoiceChannel::PlayRingbackTone_w(uint32 ssrc, bool play, bool loop) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - if (play) { - LOG(LS_INFO) << "Playing ringback tone, loop=" << loop; - } else { - LOG(LS_INFO) << "Stopping ringback tone"; - } - return media_channel()->PlayRingbackTone(ssrc, play, loop); -} - -void VoiceChannel::HandleEarlyMediaTimeout() { - // This occurs on the main thread, not the worker thread. - if (!received_media_) { - LOG(LS_INFO) << "No early media received before timeout"; - SignalEarlyMediaTimeout(this); - } -} - -bool VoiceChannel::PressDTMF_w(int digit, bool playout) { - if (!enabled() || !writable()) { - return false; - } - - return media_channel()->PressDTMF(digit, playout); -} - -void VoiceChannel::OnMessage(talk_base::Message *pmsg) { - switch (pmsg->message_id) { - case MSG_ADDSTREAM: { - StreamMessageData* data = static_cast(pmsg->pdata); - AddStream_w(data->ssrc1); - break; - } - case MSG_SETRINGBACKTONE: { - SetRingbackToneMessageData* data = - static_cast(pmsg->pdata); - data->result = SetRingbackTone_w(data->buf, data->len); - break; - } - case MSG_PLAYRINGBACKTONE: { - PlayRingbackToneMessageData* data = - static_cast(pmsg->pdata); - data->result = PlayRingbackTone_w(data->ssrc, data->play, data->loop); - break; - } - case MSG_EARLYMEDIATIMEOUT: - HandleEarlyMediaTimeout(); - break; - case MSG_PRESSDTMF: { - DtmfMessageData* data = static_cast(pmsg->pdata); - data->result = PressDTMF_w(data->digit, data->playout); - break; - } - case MSG_CHANNEL_ERROR: { - VoiceChannelErrorMessageData* data = - static_cast(pmsg->pdata); - SignalMediaError(this, data->ssrc, data->error); - delete data; - break; - } - - default: - BaseChannel::OnMessage(pmsg); - break; - } -} - -void VoiceChannel::OnConnectionMonitorUpdate( - SocketMonitor* monitor, const std::vector& infos) { - SignalConnectionMonitor(this, infos); -} - -void VoiceChannel::OnMediaMonitorUpdate( - VoiceMediaChannel* media_channel, const VoiceMediaInfo& info) { - ASSERT(media_channel == this->media_channel()); - SignalMediaMonitor(this, info); -} - -void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor* monitor, - const AudioInfo& info) { - SignalAudioMonitor(this, info); -} - -void VoiceChannel::OnVoiceChannelError( - uint32 ssrc, VoiceMediaChannel::Error error) { - VoiceChannelErrorMessageData *data = new VoiceChannelErrorMessageData( - ssrc, error); - signaling_thread()->Post(this, MSG_CHANNEL_ERROR, data); -} - -void VoiceChannel::OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, - SrtpFilter::Error error) { - switch (error) { - case SrtpFilter::ERROR_FAIL: - OnVoiceChannelError(ssrc, (mode == SrtpFilter::PROTECT) ? - VoiceMediaChannel::ERROR_REC_SRTP_ERROR : - VoiceMediaChannel::ERROR_PLAY_SRTP_ERROR); - break; - case SrtpFilter::ERROR_AUTH: - OnVoiceChannelError(ssrc, (mode == SrtpFilter::PROTECT) ? - VoiceMediaChannel::ERROR_REC_SRTP_AUTH_FAILED : - VoiceMediaChannel::ERROR_PLAY_SRTP_AUTH_FAILED); - break; - case SrtpFilter::ERROR_REPLAY: - // Only receving channel should have this error. - ASSERT(mode == SrtpFilter::UNPROTECT); - OnVoiceChannelError(ssrc, VoiceMediaChannel::ERROR_PLAY_SRTP_REPLAY); - break; - default: - break; - } -} - -VideoChannel::VideoChannel(talk_base::Thread* thread, - MediaEngine* media_engine, - VideoMediaChannel* media_channel, - BaseSession* session, - const std::string& content_name, - bool rtcp, - VoiceChannel* voice_channel) - : BaseChannel(thread, media_engine, media_channel, session, content_name, - session->CreateChannel(content_name, "video_rtp")), - voice_channel_(voice_channel), renderer_(NULL) { - if (rtcp) { - set_rtcp_transport_channel( - session->CreateChannel(content_name, "video_rtcp")); - } - // Can't go in BaseChannel because certain session states will - // trigger pure virtual functions, such as GetFirstContent() - OnSessionState(session, session->state()); - - media_channel->SignalMediaError.connect( - this, &VideoChannel::OnVideoChannelError); - srtp_filter()->SignalSrtpError.connect( - this, &VideoChannel::OnSrtpError); -} - -void VoiceChannel::SendLastMediaError() { - uint32 ssrc; - VoiceMediaChannel::Error error; - media_channel()->GetLastMediaError(&ssrc, &error); - SignalMediaError(this, ssrc, error); -} - -VideoChannel::~VideoChannel() { - StopMediaMonitor(); - // this can't be done in the base class, since it calls a virtual - DisableMedia_w(); -} - -bool VideoChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) { - StreamMessageData data(ssrc, voice_ssrc); - Send(MSG_ADDSTREAM, &data); - return true; -} - -bool VideoChannel::SetRenderer(uint32 ssrc, VideoRenderer* renderer) { - RenderMessageData data(ssrc, renderer); - Send(MSG_SETRENDERER, &data); - return true; -} - - - -bool VideoChannel::SendIntraFrame() { - Send(MSG_SENDINTRAFRAME); - return true; -} -bool VideoChannel::RequestIntraFrame() { - Send(MSG_REQUESTINTRAFRAME); - return true; -} - -void VideoChannel::ChangeState() { - // render incoming data if we are the active call - // we receive data on the default channel and multiplexed streams - bool recv = enabled(); - if (!media_channel()->SetRender(recv)) { - LOG(LS_ERROR) << "Failed to SetRender on video channel"; - // TODO: Report error back to server. - } - - // send outgoing data if we are the active call, have the - // remote party's codec, and have a writable transport - // we only send data on the default channel - bool send = enabled() && has_codec() && writable(); - if (!media_channel()->SetSend(send)) { - LOG(LS_ERROR) << "Failed to SetSend on video channel"; - // TODO: Report error back to server. - } - - LOG(LS_INFO) << "Changing video state, recv=" << recv << " send=" << send; -} - -void VideoChannel::StartMediaMonitor(int cms) { - media_monitor_.reset(new VideoMediaMonitor(media_channel(), worker_thread(), - talk_base::Thread::Current())); - media_monitor_->SignalUpdate.connect( - this, &VideoChannel::OnMediaMonitorUpdate); - media_monitor_->Start(cms); -} - -void VideoChannel::StopMediaMonitor() { - if (media_monitor_.get()) { - media_monitor_->Stop(); - media_monitor_.reset(); - } -} - -const MediaContentDescription* VideoChannel::GetFirstContent( - const SessionDescription* sdesc) { - const ContentInfo* cinfo = GetFirstVideoContent(sdesc); - if (cinfo == NULL) - return NULL; - - return static_cast(cinfo->description); -} - -bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, - ContentAction action) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - LOG(LS_INFO) << "Setting local video description"; - - const VideoContentDescription* video = - static_cast(content); - ASSERT(video != NULL); - - bool ret; - if (video->ssrc_set()) { - media_channel()->SetSendSsrc(video->ssrc()); - LOG(LS_INFO) << "Set send ssrc for video: " << video->ssrc(); - } - // set SRTP - ret = SetSrtp_w(video->cryptos(), action, CS_LOCAL); - - // set RTCP mux - if (ret) - ret = SetRtcpMux_w(video->rtcp_mux(), action, CS_LOCAL); - - // set payload types and config for receiving video - if (ret) - ret = media_channel()->SetRecvCodecs(video->codecs()); - - if (ret && video->rtp_header_extensions_set()) { - ret = media_channel()->SetRecvRtpHeaderExtensions( - video->rtp_header_extensions()); - } - - return ret; -} - -bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - LOG(LS_INFO) << "Setting remote video description"; - - const VideoContentDescription* video = - static_cast(content); - ASSERT(video != NULL); - - // set SRTP - bool ret = SetSrtp_w(video->cryptos(), action, CS_REMOTE); - // set RTCP mux - if (ret) { - ret = SetRtcpMux_w(video->rtcp_mux(), action, CS_REMOTE); - } - - // Set the send codecs before we can tweak bandwidth parameters. - // Otherwise the send_codec in the media channel won't be initialized - // and we can't set the bandwidth. - if (ret) { - ret = media_channel()->SetSendCodecs(video->codecs()); - } - - // Set video bandwidth parameters. - if (ret) { - int bandwidth_bps = video->bandwidth(); - bool auto_bandwidth = (bandwidth_bps == kAutoBandwidth); - // Ignore errors from SetSendBandwidth. - // TODO(mallinath): SetSendCodec has already been called, so this call - // may fail. - /*ret = */media_channel()->SetSendBandwidth(auto_bandwidth, bandwidth_bps); - } - // set header extensions - if (ret && video->rtp_header_extensions_set()) { - ret = media_channel()->SetSendRtpHeaderExtensions( - video->rtp_header_extensions()); - } - if (ret) { - set_has_codec(true); - ChangeState(); - } - return ret; -} - -void VideoChannel::AddStream_w(uint32 ssrc, uint32 voice_ssrc) { - media_channel()->AddStream(ssrc, voice_ssrc); -} - -void VideoChannel::RemoveStream_w(uint32 ssrc) { - media_channel()->RemoveStream(ssrc); -} - -void VideoChannel::SetRenderer_w(uint32 ssrc, VideoRenderer* renderer) { - media_channel()->SetRenderer(ssrc, renderer); -} - - -void VideoChannel::OnMessage(talk_base::Message *pmsg) { - switch (pmsg->message_id) { - case MSG_ADDSTREAM: { - StreamMessageData* data = static_cast(pmsg->pdata); - AddStream_w(data->ssrc1, data->ssrc2); - break; - } - case MSG_SETRENDERER: { - RenderMessageData* data = static_cast(pmsg->pdata); - SetRenderer_w(data->ssrc, data->renderer); - break; - } - case MSG_SENDINTRAFRAME: - SendIntraFrame_w(); - break; - case MSG_REQUESTINTRAFRAME: - RequestIntraFrame_w(); - break; - case MSG_CHANNEL_ERROR: { - const VideoChannelErrorMessageData* data = - static_cast(pmsg->pdata); - SignalMediaError(this, data->ssrc, data->error); - delete data; - break; - } - default: - BaseChannel::OnMessage(pmsg); - break; - } -} - -void VideoChannel::OnConnectionMonitorUpdate( - SocketMonitor *monitor, const std::vector &infos) { - SignalConnectionMonitor(this, infos); -} - -void VideoChannel::OnMediaMonitorUpdate( - VideoMediaChannel* media_channel, const VideoMediaInfo &info) { - ASSERT(media_channel == this->media_channel()); - SignalMediaMonitor(this, info); -} - - -void VideoChannel::OnVideoChannelError(uint32 ssrc, - VideoMediaChannel::Error error) { - VideoChannelErrorMessageData* data = new VideoChannelErrorMessageData( - ssrc, error); - signaling_thread()->Post(this, MSG_CHANNEL_ERROR, data); -} - -void VideoChannel::OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, - SrtpFilter::Error error) { - switch (error) { - case SrtpFilter::ERROR_FAIL: - OnVideoChannelError(ssrc, (mode == SrtpFilter::PROTECT) ? - VideoMediaChannel::ERROR_REC_SRTP_ERROR : - VideoMediaChannel::ERROR_PLAY_SRTP_ERROR); - break; - case SrtpFilter::ERROR_AUTH: - OnVideoChannelError(ssrc, (mode == SrtpFilter::PROTECT) ? - VideoMediaChannel::ERROR_REC_SRTP_AUTH_FAILED : - VideoMediaChannel::ERROR_PLAY_SRTP_AUTH_FAILED); - break; - case SrtpFilter::ERROR_REPLAY: - // Only receving channel should have this error. - ASSERT(mode == SrtpFilter::UNPROTECT); - // TODO: Turn on the signaling of replay error once we have - // switched to the new mechanism for doing video retransmissions. - // OnVideoChannelError(ssrc, VideoMediaChannel::ERROR_PLAY_SRTP_REPLAY); - break; - default: - break; - } -} - -} // namespace cricket diff --git a/third_party_mods/libjingle/source/talk/session/phone/devicemanager.cc b/third_party_mods/libjingle/source/talk/session/phone/devicemanager.cc index 6eca88f0e..70c07d36c 100644 --- a/third_party_mods/libjingle/source/talk/session/phone/devicemanager.cc +++ b/third_party_mods/libjingle/source/talk/session/phone/devicemanager.cc @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2011, Google Inc. + * Copyright 2004--2008, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -31,25 +31,15 @@ #include #include #include // must come before ks.h -#include #include #include +#define INITGUID // For PKEY_AudioEndpoint_GUID #include +#include #include #include #include "talk/base/win32.h" // ToUtf8 #include "talk/base/win32window.h" - -// PKEY_AudioEndpoint_GUID isn't included in uuid.lib and we don't want -// to define INITGUID in order to define all the uuids in this object file -// as it will conflict with uuid.lib (multiply defined symbols). -// So our workaround is to define this one missing symbol here manually. -EXTERN_C const PROPERTYKEY PKEY_AudioEndpoint_GUID = { { - 0x1da5d803, 0xd492, 0x4edd, { - 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e - } }, 4 -}; - #elif OSX #include #include @@ -79,14 +69,7 @@ namespace cricket { // Initialize to empty string. const std::string DeviceManager::kDefaultDeviceName; -#ifdef PLATFORM_CHROMIUM -class DeviceWatcher { - public: - explicit DeviceWatcher(DeviceManager* dm); - bool Start(); - void Stop(); -}; -#elif defined(WIN32) +#ifdef WIN32 class DeviceWatcher : public talk_base::Win32Window { public: explicit DeviceWatcher(DeviceManager* dm); @@ -135,8 +118,11 @@ class DeviceWatcher { }; #endif +#if defined(CHROMEOS) +static bool ShouldAudioDeviceBeIgnored(const std::string& device_name); +#endif #if !defined(LINUX) && !defined(IOS) -static bool ShouldDeviceBeIgnored(const std::string& device_name); +static bool ShouldVideoDeviceBeIgnored(const std::string& device_name); #endif #ifndef OSX static bool GetVideoDevices(std::vector* out); @@ -180,7 +166,7 @@ DeviceManager::~DeviceManager() { bool DeviceManager::Init() { if (!initialized_) { -#if defined(WIN32) && !defined(PLATFORM_CHROMIUM) +#if defined(WIN32) HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); need_couninitialize_ = SUCCEEDED(hr); if (FAILED(hr)) { @@ -201,7 +187,7 @@ bool DeviceManager::Init() { void DeviceManager::Terminate() { if (initialized_) { watcher_->Stop(); -#if defined(WIN32) && !defined(PLATFORM_CHROMIUM) +#if defined(WIN32) if (need_couninitialize_) { CoUninitialize(); need_couninitialize_ = false; @@ -244,16 +230,13 @@ bool DeviceManager::GetAudioOutputDevice(const std::string& name, Device* out) { #ifdef OSX static bool FilterDevice(const Device& d) { - return ShouldDeviceBeIgnored(d.name); + return ShouldVideoDeviceBeIgnored(d.name); } #endif bool DeviceManager::GetVideoCaptureDevices(std::vector* devices) { devices->clear(); -#ifdef PLATFORM_CHROMIUM - devices->push_back(Device("", -1)); - return true; -#elif OSX +#ifdef OSX if (GetQTKitVideoDevices(devices)) { // Now filter out any known incompatible devices devices->erase(remove_if(devices->begin(), devices->end(), FilterDevice), @@ -268,10 +251,7 @@ bool DeviceManager::GetVideoCaptureDevices(std::vector* devices) { bool DeviceManager::GetDefaultVideoCaptureDevice(Device* device) { bool ret = false; -#ifdef PLATFORM_CHROMIUM - *device = Device("", -1); - ret = true; -#elif WIN32 +#if WIN32 // If there are multiple capture devices, we want the first USB one. // This avoids issues with defaulting to virtual cameras or grabber cards. std::vector devices; @@ -309,10 +289,6 @@ bool DeviceManager::GetVideoCaptureDevice(const std::string& name, return false; } -#ifdef PLATFORM_CHROMIUM - *out = Device(name, name); - return true; -#else for (std::vector::const_iterator it = devices.begin(); it != devices.end(); ++it) { if (name == it->name) { @@ -320,7 +296,6 @@ bool DeviceManager::GetVideoCaptureDevice(const std::string& name, return true; } } -#endif return false; } @@ -352,10 +327,7 @@ bool DeviceManager::GetAudioDevice(bool is_input, const std::string& name, bool DeviceManager::GetAudioDevicesByPlatform(bool input, std::vector* devs) { devs->clear(); -#ifdef PLATFORM_CHROMIUM - devs->push_back(Device("", -1)); - return true; -#elif defined(LINUX_SOUND_USED) +#if defined(LINUX_SOUND_USED) if (!sound_system_.get()) { return false; } @@ -378,7 +350,14 @@ bool DeviceManager::GetAudioDevicesByPlatform(bool input, for (SoundSystemInterface::SoundDeviceLocatorList::iterator i = list.begin(); i != list.end(); ++i, ++index) { - devs->push_back(Device((*i)->name(), index)); +#if defined(CHROMEOS) + // On ChromeOS, we ignore ALSA surround and S/PDIF devices. + if (!ShouldAudioDeviceBeIgnored((*i)->device_name())) { +#endif + devs->push_back(Device((*i)->name(), index)); +#if defined(CHROMEOS) + } +#endif } SoundSystemInterface::ClearSoundDeviceLocatorList(&list); sound_system_.release(); @@ -409,18 +388,7 @@ bool DeviceManager::GetAudioDevicesByPlatform(bool input, #endif } -#if defined(PLATFORM_CHROMIUM) -DeviceWatcher::DeviceWatcher(DeviceManager* manager) { -} - -bool DeviceWatcher::Start() { - return true; -} - -void DeviceWatcher::Stop() { -} - -#elif defined(WIN32) +#if defined(WIN32) bool GetVideoDevices(std::vector* devices) { return GetDevices(CLSID_VideoInputDeviceCategory, devices); } @@ -452,7 +420,7 @@ bool GetDevices(const CLSID& catid, std::vector* devices) { if (SUCCEEDED(bag->Read(kFriendlyName, &name, 0)) && name.vt == VT_BSTR) { name_str = talk_base::ToUtf8(name.bstrVal); - if (!ShouldDeviceBeIgnored(name_str)) { + if (!ShouldVideoDeviceBeIgnored(name_str)) { // Get the device id if one exists. if (SUCCEEDED(bag->Read(kDevicePath, &path, 0)) && path.vt == VT_BSTR) { @@ -999,11 +967,32 @@ bool DeviceWatcher::IsDescriptorClosed() { #endif +#if defined(CHROMEOS) +// Checks if we want to ignore this audio device. +static bool ShouldAudioDeviceBeIgnored(const std::string& device_name) { + static const char* const kFilteredAudioDevicesName[] = { + "surround40:", + "surround41:", + "surround50:", + "surround51:", + "surround71:", + "iec958:" // S/PDIF + }; + for (int i = 0; i < ARRAY_SIZE(kFilteredAudioDevicesName); ++i) { + if (0 == device_name.find(kFilteredAudioDevicesName[i])) { + LOG(LS_INFO) << "Ignoring device " << device_name; + return true; + } + } + return false; +} +#endif + // TODO: Try to get hold of a copy of Final Cut to understand why we // crash while scanning their components on OS X. #if !defined(LINUX) && !defined(IOS) -static bool ShouldDeviceBeIgnored(const std::string& device_name) { - static const char* const kFilteredDevices[] = { +static bool ShouldVideoDeviceBeIgnored(const std::string& device_name) { + static const char* const kFilteredVideoDevicesName[] = { "Google Camera Adapter", // Our own magiccams #ifdef WIN32 "Asus virtual Camera", // Bad Asus desktop virtual cam @@ -1014,9 +1003,9 @@ static bool ShouldDeviceBeIgnored(const std::string& device_name) { #endif }; - for (int i = 0; i < ARRAY_SIZE(kFilteredDevices); ++i) { - if (strnicmp(device_name.c_str(), kFilteredDevices[i], - strlen(kFilteredDevices[i])) == 0) { + for (int i = 0; i < ARRAY_SIZE(kFilteredVideoDevicesName); ++i) { + if (strnicmp(device_name.c_str(), kFilteredVideoDevicesName[i], + strlen(kFilteredVideoDevicesName[i])) == 0) { LOG(LS_INFO) << "Ignoring device " << device_name; return true; } diff --git a/third_party_mods/libjingle/source/talk/session/phone/devicemanager.h b/third_party_mods/libjingle/source/talk/session/phone/devicemanager.h index 3dd1916fb..9d2948ee6 100644 --- a/third_party_mods/libjingle/source/talk/session/phone/devicemanager.h +++ b/third_party_mods/libjingle/source/talk/session/phone/devicemanager.h @@ -42,8 +42,7 @@ namespace cricket { class DeviceWatcher; // Used to represent an audio or video capture or render device. -class Device { - public: +struct Device { Device() {} Device(const std::string& first, int second) : name(first), diff --git a/third_party_mods/libjingle/source/talk/session/phone/filemediaengine.h b/third_party_mods/libjingle/source/talk/session/phone/filemediaengine.h deleted file mode 100644 index 2ece53a53..000000000 --- a/third_party_mods/libjingle/source/talk/session/phone/filemediaengine.h +++ /dev/null @@ -1,221 +0,0 @@ -// libjingle -// Copyright 2004--2005, Google Inc. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// 3. The name of the author may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef TALK_SESSION_PHONE_FILEMEDIAENGINE_H_ -#define TALK_SESSION_PHONE_FILEMEDIAENGINE_H_ - -#include -#include - -#include "talk/base/scoped_ptr.h" -#include "talk/session/phone/codec.h" -#include "talk/session/phone/mediachannel.h" -#include "talk/session/phone/mediaengine.h" - -namespace talk_base { -class StreamInterface; -} - -namespace cricket { - -// A media engine contains a capturer, an encoder, and a sender in the sender -// side and a receiver, a decoder, and a renderer in the receiver side. -// FileMediaEngine simulates the capturer and the encoder via an input RTP dump -// stream and simulates the decoder and the renderer via an output RTP dump -// stream. Depending on the parameters of the constructor, FileMediaEngine can -// act as file voice engine, file video engine, or both. Currently, we use -// only the RTP dump packets. TODO: Enable RTCP packets. -class FileMediaEngine : public MediaEngine { - public: - FileMediaEngine() {} - virtual ~FileMediaEngine() {} - - // Set the file name of the input or output RTP dump for voice or video. - // Should be called before the channel is created. - void set_voice_input_filename(const std::string& filename) { - voice_input_filename_ = filename; - } - void set_voice_output_filename(const std::string& filename) { - voice_output_filename_ = filename; - } - void set_video_input_filename(const std::string& filename) { - video_input_filename_ = filename; - } - void set_video_output_filename(const std::string& filename) { - video_output_filename_ = filename; - } - - // Should be called before codecs() and video_codecs() are called. We need to - // set the voice and video codecs; otherwise, Jingle initiation will fail. - void set_voice_codecs(const std::vector& codecs) { - voice_codecs_ = codecs; - } - void set_video_codecs(const std::vector& codecs) { - video_codecs_ = codecs; - } - - // Implement pure virtual methods of MediaEngine. - virtual bool Init() { return true; } - virtual void Terminate() {} - virtual int GetCapabilities(); - virtual VoiceMediaChannel* CreateChannel(); - virtual VideoMediaChannel* CreateVideoChannel(VoiceMediaChannel* voice_ch); - virtual SoundclipMedia* CreateSoundclip() { return NULL; } - virtual bool SetAudioOptions(int options) { return true; } - virtual bool SetVideoOptions(int options) { return true; } - virtual bool SetDefaultVideoEncoderConfig(const VideoEncoderConfig& config) { - return true; - } - virtual bool SetSoundDevices(const Device* in_dev, const Device* out_dev) { - return true; - } - virtual bool SetVideoCaptureDevice(const Device* cam_device) { return true; } - virtual bool GetOutputVolume(int* level) { *level = 0; return true; } - virtual bool SetOutputVolume(int level) { return true; } - virtual int GetInputLevel() { return 0; } - virtual bool SetLocalMonitor(bool enable) { return true; } - virtual bool SetLocalRenderer(VideoRenderer* renderer) { return true; } - // TODO: control channel send? - virtual CaptureResult SetVideoCapture(bool capture) { return CR_SUCCESS; } - virtual const std::vector& audio_codecs() { - return voice_codecs_; - } - virtual const std::vector& video_codecs() { - return video_codecs_; - } - virtual bool FindAudioCodec(const AudioCodec& codec) { return true; } - virtual bool FindVideoCodec(const VideoCodec& codec) { return true; } - virtual void SetVoiceLogging(int min_sev, const char* filter) {} - virtual void SetVideoLogging(int min_sev, const char* filter) {} - - private: - std::string voice_input_filename_; - std::string voice_output_filename_; - std::string video_input_filename_; - std::string video_output_filename_; - std::vector voice_codecs_; - std::vector video_codecs_; - - DISALLOW_COPY_AND_ASSIGN(FileMediaEngine); -}; - -class RtpSenderReceiver; // Forward declaration. Defined in the .cc file. - -class FileVoiceChannel : public VoiceMediaChannel { - public: - FileVoiceChannel(const std::string& in_file, const std::string& out_file); - virtual ~FileVoiceChannel(); - - // Implement pure virtual methods of VoiceMediaChannel. - virtual bool SetRecvCodecs(const std::vector& codecs) { - return true; - } - virtual bool SetSendCodecs(const std::vector& codecs); - virtual bool SetRecvRtpHeaderExtensions( - const std::vector& extensions) { - return true; - } - virtual bool SetSendRtpHeaderExtensions( - const std::vector& extensions) { - return true; - } - virtual bool SetPlayout(bool playout) { return true; } - virtual bool SetSend(SendFlags flag); - virtual bool AddStream(uint32 ssrc) { return true; } - virtual bool RemoveStream(uint32 ssrc) { return true; } - virtual bool GetActiveStreams(AudioInfo::StreamList* actives) { return true; } - virtual int GetOutputLevel() { return 0; } - virtual bool SetRingbackTone(const char* buf, int len) { return true; } - virtual bool PlayRingbackTone(uint32 ssrc, bool play, bool loop) { - return true; - } - virtual bool PressDTMF(int event, bool playout) { return true; } - virtual bool GetStats(VoiceMediaInfo* info) { return true; } - - // Implement pure virtual methods of MediaChannel. - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet) {} - virtual void SetSendSsrc(uint32 id) {} // TODO: change RTP packet? - virtual bool SetRtcpCName(const std::string& cname) { return true; } - virtual bool Mute(bool on) { return false; } - virtual bool SetSendBandwidth(bool autobw, int bps) { return true; } - virtual bool SetOptions(int options) { return true; } - virtual int GetMediaChannelId() { return -1; } - - private: - talk_base::scoped_ptr rtp_sender_receiver_; - DISALLOW_COPY_AND_ASSIGN(FileVoiceChannel); -}; - -class FileVideoChannel : public VideoMediaChannel { - public: - FileVideoChannel(const std::string& in_file, const std::string& out_file); - virtual ~FileVideoChannel(); - - // Implement pure virtual methods of VideoMediaChannel. - virtual bool SetRecvCodecs(const std::vector& codecs) { - return true; - } - virtual bool SetSendCodecs(const std::vector& codecs); - virtual bool SetRecvRtpHeaderExtensions( - const std::vector& extensions) { - return true; - } - virtual bool SetSendRtpHeaderExtensions( - const std::vector& extensions) { - return true; - } - virtual bool SetRender(bool render) { return true; } - virtual bool SetSend(bool send); - virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc) { return true; } - virtual bool RemoveStream(uint32 ssrc) { return true; } - virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer) { - return true; - } - virtual bool SetExternalRenderer(uint32 ssrc, void* renderer) { - return true; - } - virtual bool GetStats(VideoMediaInfo* info) { return true; } - virtual bool SendIntraFrame() { return false; } - virtual bool RequestIntraFrame() { return false; } - - // Implement pure virtual methods of MediaChannel. - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet) {} - virtual void SetSendSsrc(uint32 id) {} // TODO: change RTP packet? - virtual bool SetRtcpCName(const std::string& cname) { return true; } - virtual bool Mute(bool on) { return false; } - virtual bool SetSendBandwidth(bool autobw, int bps) { return true; } - virtual bool SetOptions(int options) { return true; } - virtual int GetMediaChannelId() { return -1; } - - private: - talk_base::scoped_ptr rtp_sender_receiver_; - DISALLOW_COPY_AND_ASSIGN(FileVideoChannel); -}; - -} // namespace cricket - -#endif // TALK_SESSION_PHONE_FILEMEDIAENGINE_H_ diff --git a/third_party_mods/libjingle/source/talk/session/phone/mediachannel.h b/third_party_mods/libjingle/source/talk/session/phone/mediachannel.h deleted file mode 100644 index 706276141..000000000 --- a/third_party_mods/libjingle/source/talk/session/phone/mediachannel.h +++ /dev/null @@ -1,501 +0,0 @@ -/* - * libjingle - * Copyright 2004--2010, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_SESSION_PHONE_MEDIACHANNEL_H_ -#define TALK_SESSION_PHONE_MEDIACHANNEL_H_ - -#include -#include - -#include "talk/base/basictypes.h" -#include "talk/base/sigslot.h" -#include "talk/base/socket.h" -#include "talk/session/phone/codec.h" -// TODO: re-evaluate this include -#include "talk/session/phone/audiomonitor.h" - -namespace talk_base { -class Buffer; -} - -namespace flute { -class MagicCamVideoRenderer; -} - -namespace cricket { - -const int kMinRtpHeaderExtensionId = 1; -const int kMaxRtpHeaderExtensionId = 255; - -struct RtpHeaderExtension { - RtpHeaderExtension(const std::string& u, int i) : uri(u), id(i) {} - std::string uri; - int id; - // TODO: SendRecv direction; -}; - -enum VoiceMediaChannelOptions { - OPT_CONFERENCE = 0x10000, // tune the audio stream for conference mode - -}; - -enum VideoMediaChannelOptions { - OPT_INTERPOLATE = 0x10000 // Increase the output framerate by 2x by - // interpolating frames -}; - -class MediaChannel : public sigslot::has_slots<> { - public: - class NetworkInterface { - public: - enum SocketType { ST_RTP, ST_RTCP }; - virtual bool SendPacket(talk_base::Buffer* packet) = 0; - virtual bool SendRtcp(talk_base::Buffer* packet) = 0; - virtual int SetOption(SocketType type, talk_base::Socket::Option opt, - int option) = 0; - virtual ~NetworkInterface() {} - }; - - MediaChannel() : network_interface_(NULL) {} - virtual ~MediaChannel() {} - - // Gets/sets the abstract inteface class for sending RTP/RTCP data. - NetworkInterface *network_interface() { return network_interface_; } - virtual void SetInterface(NetworkInterface *iface) { - network_interface_ = iface; - } - - // Called when a RTP packet is received. - virtual void OnPacketReceived(talk_base::Buffer* packet) = 0; - // Called when a RTCP packet is received. - virtual void OnRtcpReceived(talk_base::Buffer* packet) = 0; - // Sets the SSRC to be used for outgoing data. - virtual void SetSendSsrc(uint32 id) = 0; - // Set the CNAME of RTCP - virtual bool SetRtcpCName(const std::string& cname) = 0; - // Mutes the channel. - virtual bool Mute(bool on) = 0; - - // Sets the RTP extension headers and IDs to use when sending RTP. - virtual bool SetRecvRtpHeaderExtensions( - const std::vector& extensions) = 0; - virtual bool SetSendRtpHeaderExtensions( - const std::vector& extensions) = 0; - // Sets the rate control to use when sending data. - virtual bool SetSendBandwidth(bool autobw, int bps) = 0; - // Sets the media options to use. - virtual bool SetOptions(int options) = 0; - // Gets the Rtc channel id - virtual int GetMediaChannelId() = 0; - - protected: - NetworkInterface *network_interface_; -}; - -enum SendFlags { - SEND_NOTHING, - SEND_RINGBACKTONE, - SEND_MICROPHONE -}; - -struct VoiceSenderInfo { - uint32 ssrc; - int bytes_sent; - int packets_sent; - int packets_lost; - float fraction_lost; - int ext_seqnum; - int rtt_ms; - int jitter_ms; - int audio_level; -}; - -struct VoiceReceiverInfo { - uint32 ssrc; - int bytes_rcvd; - int packets_rcvd; - int packets_lost; - float fraction_lost; - int ext_seqnum; - int jitter_ms; - int jitter_buffer_ms; - int jitter_buffer_preferred_ms; - int delay_estimate_ms; - int audio_level; -}; - -struct VideoSenderInfo { - uint32 ssrc; - int bytes_sent; - int packets_sent; - int packets_cached; - int packets_lost; - float fraction_lost; - int firs_rcvd; - int nacks_rcvd; - int rtt_ms; - int frame_width; - int frame_height; - int framerate_input; - int framerate_sent; - int nominal_bitrate; - int preferred_bitrate; -}; - -struct VideoReceiverInfo { - uint32 ssrc; - int bytes_rcvd; - // vector layer_bytes_rcvd; - int packets_rcvd; - int packets_lost; - int packets_concealed; - float fraction_lost; - int firs_sent; - int nacks_sent; - int frame_width; - int frame_height; - int framerate_rcvd; - int framerate_decoded; - int framerate_output; -}; - -struct BandwidthEstimationInfo { - int available_send_bandwidth; - int available_recv_bandwidth; - int target_enc_bitrate; - int actual_enc_bitrate; - int retransmit_bitrate; - int transmit_bitrate; - int bucket_delay; -}; - -struct VoiceMediaInfo { - void Clear() { - senders.clear(); - receivers.clear(); - } - std::vector senders; - std::vector receivers; -}; - -struct VideoMediaInfo { - void Clear() { - senders.clear(); - receivers.clear(); - bw_estimations.clear(); - } - std::vector senders; - std::vector receivers; - std::vector bw_estimations; -}; - -class VoiceMediaChannel : public MediaChannel { - public: - enum Error { - ERROR_NONE = 0, // No error. - ERROR_OTHER, // Other errors. - ERROR_REC_DEVICE_OPEN_FAILED = 100, // Could not open mic. - ERROR_REC_DEVICE_MUTED, // Mic was muted by OS. - ERROR_REC_DEVICE_SILENT, // No background noise picked up. - ERROR_REC_DEVICE_SATURATION, // Mic input is clipping. - ERROR_REC_DEVICE_REMOVED, // Mic was removed while active. - ERROR_REC_RUNTIME_ERROR, // Processing is encountering errors. - ERROR_REC_SRTP_ERROR, // Generic SRTP failure. - ERROR_REC_SRTP_AUTH_FAILED, // Failed to authenticate packets. - ERROR_REC_TYPING_NOISE_DETECTED, // Typing noise is detected. - ERROR_PLAY_DEVICE_OPEN_FAILED = 200, // Could not open playout. - ERROR_PLAY_DEVICE_MUTED, // Playout muted by OS. - ERROR_PLAY_DEVICE_REMOVED, // Playout removed while active. - ERROR_PLAY_RUNTIME_ERROR, // Errors in voice processing. - ERROR_PLAY_SRTP_ERROR, // Generic SRTP failure. - ERROR_PLAY_SRTP_AUTH_FAILED, // Failed to authenticate packets. - ERROR_PLAY_SRTP_REPLAY, // Packet replay detected. - }; - - VoiceMediaChannel() {} - virtual ~VoiceMediaChannel() {} - // Sets the codecs/payload types to be used for incoming media. - virtual bool SetRecvCodecs(const std::vector& codecs) = 0; - // Sets the codecs/payload types to be used for outgoing media. - virtual bool SetSendCodecs(const std::vector& codecs) = 0; - // Starts or stops playout of received audio. - virtual bool SetPlayout(bool playout) = 0; - // Starts or stops sending (and potentially capture) of local audio. - virtual bool SetSend(SendFlags flag) = 0; - // Adds a new receive-only stream with the specified SSRC. - virtual bool AddStream(uint32 ssrc) = 0; - // Removes a stream added with AddStream. - virtual bool RemoveStream(uint32 ssrc) = 0; - // Gets current energy levels for all incoming streams. - virtual bool GetActiveStreams(AudioInfo::StreamList* actives) = 0; - // Get the current energy level for the outgoing stream. - virtual int GetOutputLevel() = 0; - // Specifies a ringback tone to be played during call setup. - virtual bool SetRingbackTone(const char *buf, int len) = 0; - // Plays or stops the aforementioned ringback tone - virtual bool PlayRingbackTone(uint32 ssrc, bool play, bool loop) = 0; - // Sends a out-of-band DTMF signal using the specified event. - virtual bool PressDTMF(int event, bool playout) = 0; - // Gets quality stats for the channel. - virtual bool GetStats(VoiceMediaInfo* info) = 0; - // Gets last reported error for this media channel. - virtual void GetLastMediaError(uint32* ssrc, - VoiceMediaChannel::Error* error) { - ASSERT(error != NULL); - *error = ERROR_NONE; - } - // Signal errors from MediaChannel. Arguments are: - // ssrc(uint32), and error(VoiceMediaChannel::Error). - sigslot::signal2 SignalMediaError; -}; - -// Represents a YUV420 (a.k.a. I420) video frame. -class VideoFrame { - friend class flute::MagicCamVideoRenderer; - - public: - VideoFrame() : rendered_(false) {} - - virtual ~VideoFrame() {} - - virtual size_t GetWidth() const = 0; - virtual size_t GetHeight() const = 0; - virtual const uint8 *GetYPlane() const = 0; - virtual const uint8 *GetUPlane() const = 0; - virtual const uint8 *GetVPlane() const = 0; - virtual uint8 *GetYPlane() = 0; - virtual uint8 *GetUPlane() = 0; - virtual uint8 *GetVPlane() = 0; - virtual int32 GetYPitch() const = 0; - virtual int32 GetUPitch() const = 0; - virtual int32 GetVPitch() const = 0; - - // For retrieving the aspect ratio of each pixel. Usually this is 1x1, but - // the aspect_ratio_idc parameter of H.264 can specify non-square pixels. - virtual size_t GetPixelWidth() const = 0; - virtual size_t GetPixelHeight() const = 0; - - // TODO: Add a fourcc format here and probably combine VideoFrame - // with CapturedFrame. - virtual int64 GetElapsedTime() const = 0; - virtual int64 GetTimeStamp() const = 0; - virtual void SetElapsedTime(int64 elapsed_time) = 0; - virtual void SetTimeStamp(int64 time_stamp) = 0; - - // Make a copy of the frame. The frame buffer itself may not be copied, - // in which case both the current and new VideoFrame will share a single - // reference-counted frame buffer. - virtual VideoFrame *Copy() const = 0; - - // Writes the frame into the given frame buffer, provided that it is of - // sufficient size. Returns the frame's actual size, regardless of whether - // it was written or not (like snprintf). If there is insufficient space, - // nothing is written. - virtual size_t CopyToBuffer(uint8 *buffer, size_t size) const = 0; - - // Converts the I420 data to RGB of a certain type such as ARGB and ABGR. - // Returns the frame's actual size, regardless of whether it was written or - // not (like snprintf). Parameters size and pitch_rgb are in units of bytes. - // If there is insufficient space, nothing is written. - virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8 *buffer, - size_t size, size_t pitch_rgb) const = 0; - - // Writes the frame into the given planes, stretched to the given width and - // height. The parameter "interpolate" controls whether to interpolate or just - // take the nearest-point. The parameter "crop" controls whether to crop this - // frame to the aspect ratio of the given dimensions before stretching. - virtual void StretchToPlanes(uint8 *y, uint8 *u, uint8 *v, - int32 pitchY, int32 pitchU, int32 pitchV, - size_t width, size_t height, - bool interpolate, bool crop) const = 0; - - // Writes the frame into the given frame buffer, stretched to the given width - // and height, provided that it is of sufficient size. Returns the frame's - // actual size, regardless of whether it was written or not (like snprintf). - // If there is insufficient space, nothing is written. The parameter - // "interpolate" controls whether to interpolate or just take the - // nearest-point. The parameter "crop" controls whether to crop this frame to - // the aspect ratio of the given dimensions before stretching. - virtual size_t StretchToBuffer(size_t w, size_t h, uint8 *buffer, size_t size, - bool interpolate, bool crop) const = 0; - - // Writes the frame into the target VideoFrame, stretched to the size of that - // frame. The parameter "interpolate" controls whether to interpolate or just - // take the nearest-point. The parameter "crop" controls whether to crop this - // frame to the aspect ratio of the target frame before stretching. - virtual void StretchToFrame(VideoFrame *target, bool interpolate, - bool crop) const = 0; - - // Stretches the frame to the given size, creating a new VideoFrame object to - // hold it. The parameter "interpolate" controls whether to interpolate or - // just take the nearest-point. The parameter "crop" controls whether to crop - // this frame to the aspect ratio of the given dimensions before stretching. - virtual VideoFrame *Stretch(size_t w, size_t h, bool interpolate, - bool crop) const = 0; - - // Size of an I420 image of given dimensions when stored as a frame buffer. - static size_t SizeOf(size_t w, size_t h) { - return w * h + ((w + 1) / 2) * ((h + 1) / 2) * 2; - } - - protected: - // The frame needs to be rendered to magiccam only once. - // TODO: Remove this flag once magiccam rendering is fully replaced - // by client3d rendering. - mutable bool rendered_; -}; - -// Simple subclass for use in mocks. -class NullVideoFrame : public VideoFrame { - public: - virtual size_t GetWidth() const { return 0; } - virtual size_t GetHeight() const { return 0; } - virtual const uint8 *GetYPlane() const { return NULL; } - virtual const uint8 *GetUPlane() const { return NULL; } - virtual const uint8 *GetVPlane() const { return NULL; } - virtual uint8 *GetYPlane() { return NULL; } - virtual uint8 *GetUPlane() { return NULL; } - virtual uint8 *GetVPlane() { return NULL; } - virtual int32 GetYPitch() const { return 0; } - virtual int32 GetUPitch() const { return 0; } - virtual int32 GetVPitch() const { return 0; } - - virtual size_t GetPixelWidth() const { return 1; } - virtual size_t GetPixelHeight() const { return 1; } - virtual int64 GetElapsedTime() const { return 0; } - virtual int64 GetTimeStamp() const { return 0; } - virtual void SetElapsedTime(int64 elapsed_time) {} - virtual void SetTimeStamp(int64 time_stamp) {} - - virtual VideoFrame *Copy() const { - return NULL; - } - - virtual size_t CopyToBuffer(uint8 *buffer, size_t size) const { - return 0; - } - - virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8 *buffer, - size_t size, size_t pitch_rgb) const { - return 0; - } - - virtual void StretchToPlanes(uint8 *y, uint8 *u, uint8 *v, - int32 pitchY, int32 pitchU, int32 pitchV, - size_t width, size_t height, - bool interpolate, bool crop) const { - } - - virtual size_t StretchToBuffer(size_t w, size_t h, uint8 *buffer, size_t size, - bool interpolate, bool crop) const { - return 0; - } - - virtual void StretchToFrame(VideoFrame *target, bool interpolate, - bool crop) const { - } - - virtual VideoFrame *Stretch(size_t w, size_t h, bool interpolate, - bool crop) const { - return NULL; - } -}; - -// Abstract interface for rendering VideoFrames. -class VideoRenderer { - public: - virtual ~VideoRenderer() {} - // Called when the video has changed size. - virtual bool SetSize(int width, int height, int reserved) = 0; - // Called when a new frame is available for display. - virtual bool RenderFrame(const VideoFrame *frame) = 0; -}; - -// Simple implementation for use in tests. -class NullVideoRenderer : public VideoRenderer { - virtual bool SetSize(int width, int height, int reserved) { - return true; - } - // Called when a new frame is available for display. - virtual bool RenderFrame(const VideoFrame *frame) { - return true; - } -}; - -class VideoMediaChannel : public MediaChannel { - public: - enum Error { - ERROR_NONE = 0, // No error. - ERROR_OTHER, // Other errors. - ERROR_REC_DEVICE_OPEN_FAILED = 100, // Could not open camera. - ERROR_REC_DEVICE_NO_DEVICE, // No camera. - ERROR_REC_DEVICE_IN_USE, // Device is in already use. - ERROR_REC_DEVICE_REMOVED, // Device is removed. - ERROR_REC_SRTP_ERROR, // Generic sender SRTP failure. - ERROR_REC_SRTP_AUTH_FAILED, // Failed to authenticate packets. - ERROR_PLAY_SRTP_ERROR = 200, // Generic receiver SRTP failure. - ERROR_PLAY_SRTP_AUTH_FAILED, // Failed to authenticate packets. - ERROR_PLAY_SRTP_REPLAY, // Packet replay detected. - }; - - VideoMediaChannel() { renderer_ = NULL; } - virtual ~VideoMediaChannel() {} - // Sets the codecs/payload types to be used for incoming media. - virtual bool SetRecvCodecs(const std::vector &codecs) = 0; - // Sets the codecs/payload types to be used for outgoing media. - virtual bool SetSendCodecs(const std::vector &codecs) = 0; - // Starts or stops playout of received video. - virtual bool SetRender(bool render) = 0; - // Starts or stops transmission (and potentially capture) of local video. - virtual bool SetSend(bool send) = 0; - // Adds a new receive-only stream with the specified SSRC. - virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc) = 0; - // Removes a stream added with AddStream. - virtual bool RemoveStream(uint32 ssrc) = 0; - // Sets the renderer object to be used for the specified stream. - // If SSRC is 0, the renderer is used for the 'default' stream. - virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer) = 0; - // Sets the renderer object to be used for the specified stream. - // If SSRC is 0, the renderer is used for the 'default' stream. - virtual bool SetExternalRenderer(uint32 ssrc, void* renderer) = 0; - // Gets quality stats for the channel. - virtual bool GetStats(VideoMediaInfo* info) = 0; - - // Send an intra frame to the receivers. - virtual bool SendIntraFrame() = 0; - // Reuqest each of the remote senders to send an intra frame. - virtual bool RequestIntraFrame() = 0; - - sigslot::signal2 SignalMediaError; - - protected: - VideoRenderer *renderer_; -}; - -} // namespace cricket - -#endif // TALK_SESSION_PHONE_MEDIACHANNEL_H_ diff --git a/third_party_mods/libjingle/source/talk/session/phone/mediaengine.cc b/third_party_mods/libjingle/source/talk/session/phone/mediaengine.cc deleted file mode 100644 index b6a872849..000000000 --- a/third_party_mods/libjingle/source/talk/session/phone/mediaengine.cc +++ /dev/null @@ -1,58 +0,0 @@ -// -// libjingle -// Copyright 2004--2007, Google Inc. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// 3. The name of the author may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -#ifdef HAVE_WEBRTC -#include "talk/app/voicemediaengine.h" -#include "talk/app/videomediaengine.h" -#endif -#include "talk/session/phone/mediaengine.h" -#ifdef HAVE_LINPHONE -#include "talk/session/phone/linphonemediaengine.h" -#endif - - -namespace cricket { - -#ifdef HAVE_WEBRTC -template<> -CompositeMediaEngine - ::CompositeMediaEngine() : video_(&voice_) { -} -MediaEngine* MediaEngine::Create() { - return new CompositeMediaEngine(); -} -#else -MediaEngine* MediaEngine::Create() { -#ifdef HAVE_LINPHONE - return new LinphoneMediaEngine("", ""); -#else - return new NullMediaEngine(); -#endif -} -#endif -}; // namespace cricket diff --git a/third_party_mods/libjingle/source/talk/session/phone/mediamessages.cc b/third_party_mods/libjingle/source/talk/session/phone/mediamessages.cc deleted file mode 100644 index b1e9b7625..000000000 --- a/third_party_mods/libjingle/source/talk/session/phone/mediamessages.cc +++ /dev/null @@ -1,242 +0,0 @@ -/* - * libjingle - * Copyright 2010, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "talk/session/phone/mediamessages.h" - -#include "talk/base/stringencode.h" -#include "talk/p2p/base/constants.h" -#include "talk/session/phone/mediasessionclient.h" -#include "talk/xmllite/xmlelement.h" - -namespace cricket { - -const NamedSource* GetFirstSourceByNick(const NamedSources& sources, - const std::string& nick) { - for (NamedSources::const_iterator source = sources.begin(); - source != sources.end(); ++source) { - if (source->nick == nick) { - return &*source; - } - } - return NULL; -} - -const NamedSource* GetSourceBySsrc(const NamedSources& sources, uint32 ssrc) { - for (NamedSources::const_iterator source = sources.begin(); - source != sources.end(); ++source) { - if (source->ssrc == ssrc) { - return &*source; - } - } - return NULL; -} - -const NamedSource* MediaSources::GetFirstAudioSourceByNick( - const std::string& nick) { - return GetFirstSourceByNick(audio, nick); -} - -const NamedSource* MediaSources::GetFirstVideoSourceByNick( - const std::string& nick) { - return GetFirstSourceByNick(video, nick); -} - -const NamedSource* MediaSources::GetAudioSourceBySsrc(uint32 ssrc) { - return GetSourceBySsrc(audio, ssrc); -} - -const NamedSource* MediaSources::GetVideoSourceBySsrc(uint32 ssrc) { - return GetSourceBySsrc(video, ssrc); -} - -// NOTE: There is no check here for duplicate sources, so check before -// adding. -void AddSource(NamedSources* sources, const NamedSource& source) { - sources->push_back(source); -} - -void MediaSources::AddAudioSource(const NamedSource& source) { - AddSource(&audio, source); -} - -void MediaSources::AddVideoSource(const NamedSource& source) { - AddSource(&video, source); -} - -void RemoveSourceBySsrc(NamedSources* sources, uint32 ssrc) { - for (NamedSources::iterator source = sources->begin(); - source != sources->end(); ) { - if (source->ssrc == ssrc) { - source = sources->erase(source); - } else { - ++source; - } - } -} - -void MediaSources::RemoveAudioSourceBySsrc(uint32 ssrc) { - RemoveSourceBySsrc(&audio, ssrc); -} - -void MediaSources::RemoveVideoSourceBySsrc(uint32 ssrc) { - RemoveSourceBySsrc(&video, ssrc); -} - -bool ParseSsrc(const std::string& string, uint32* ssrc) { - return talk_base::FromString(string, ssrc); -} - -bool ParseSsrc(const buzz::XmlElement* element, uint32* ssrc) { - if (element == NULL) { - return false; - } - return ParseSsrc(element->BodyText(), ssrc); -} - -bool ParseNamedSource(const buzz::XmlElement* source_elem, - NamedSource* named_source, - ParseError* error) { - named_source->nick = source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_NICK); - if (named_source->nick.empty()) { - return BadParse("Missing or invalid nick.", error); - } - - named_source->name = source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_NAME); - named_source->usage = source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_USAGE); - named_source->removed = - (STR_JINGLE_DRAFT_SOURCE_STATE_REMOVED == - source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_STATE)); - - const buzz::XmlElement* ssrc_elem = - source_elem->FirstNamed(QN_JINGLE_DRAFT_SOURCE_SSRC); - if (ssrc_elem != NULL && !ssrc_elem->BodyText().empty()) { - uint32 ssrc; - if (!ParseSsrc(ssrc_elem->BodyText(), &ssrc)) { - return BadParse("Missing or invalid ssrc.", error); - } - named_source->SetSsrc(ssrc); - } - - return true; -} - -bool IsSourcesNotify(const buzz::XmlElement* action_elem) { - return action_elem->FirstNamed(QN_JINGLE_DRAFT_NOTIFY) != NULL; -} - -bool ParseSourcesNotify(const buzz::XmlElement* action_elem, - const SessionDescription* session_description, - MediaSources* sources, - ParseError* error) { - for (const buzz::XmlElement* notify_elem - = action_elem->FirstNamed(QN_JINGLE_DRAFT_NOTIFY); - notify_elem != NULL; - notify_elem = notify_elem->NextNamed(QN_JINGLE_DRAFT_NOTIFY)) { - std::string content_name = notify_elem->Attr(QN_JINGLE_DRAFT_CONTENT_NAME); - for (const buzz::XmlElement* source_elem - = notify_elem->FirstNamed(QN_JINGLE_DRAFT_SOURCE); - source_elem != NULL; - source_elem = source_elem->NextNamed(QN_JINGLE_DRAFT_SOURCE)) { - NamedSource named_source; - if (!ParseNamedSource(source_elem, &named_source, error)) { - return false; - } - - if (session_description == NULL) { - return BadParse("unknown content name: " + content_name, error); - } - const ContentInfo* content = - FindContentInfoByName(session_description->contents(), content_name); - if (content == NULL) { - return BadParse("unknown content name: " + content_name, error); - } - - if (IsAudioContent(content)) { - sources->audio.push_back(named_source); - } else if (IsVideoContent(content)) { - sources->video.push_back(named_source); - } - } - } - - return true; -} - -buzz::XmlElement* CreateViewElem(const std::string& name, - const std::string& type) { - buzz::XmlElement* view_elem = - new buzz::XmlElement(QN_JINGLE_DRAFT_VIEW, true); - view_elem->AddAttr(QN_JINGLE_DRAFT_CONTENT_NAME, name); - view_elem->SetAttr(QN_JINGLE_DRAFT_VIEW_TYPE, type); - return view_elem; -} - -buzz::XmlElement* CreateVideoViewElem(const std::string& content_name, - const std::string& type) { - return CreateViewElem(content_name, type); -} - -buzz::XmlElement* CreateNoneVideoViewElem(const std::string& content_name) { - return CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_NONE); -} - -buzz::XmlElement* CreateStaticVideoViewElem(const std::string& content_name, - const StaticVideoView& view) { - buzz::XmlElement* view_elem = - CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_STATIC); - AddXmlAttr(view_elem, QN_JINGLE_DRAFT_VIEW_SSRC, view.ssrc); - - buzz::XmlElement* params_elem = new buzz::XmlElement( - QN_JINGLE_DRAFT_VIEW_PARAMS); - AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_WIDTH, view.width); - AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_HEIGHT, view.height); - AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_FRAMERATE, - view.framerate); - AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_PREFERENCE, - view.preference); - view_elem->AddElement(params_elem); - - return view_elem; -} - -bool WriteViewRequest(const std::string& content_name, - const ViewRequest& request, - XmlElements* elems, - WriteError* error) { - if (request.static_video_views.size() == 0) { - elems->push_back(CreateNoneVideoViewElem(content_name)); - } else { - for (StaticVideoViews::const_iterator view = - request.static_video_views.begin(); - view != request.static_video_views.end(); ++view) { - elems->push_back(CreateStaticVideoViewElem(content_name, *view)); - } - } - return true; -} - -} // namespace cricket diff --git a/third_party_mods/libjingle/source/talk/session/phone/mediamessages.h b/third_party_mods/libjingle/source/talk/session/phone/mediamessages.h deleted file mode 100644 index 58e879376..000000000 --- a/third_party_mods/libjingle/source/talk/session/phone/mediamessages.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * libjingle - * Copyright 2010, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_SESSION_PHONE_MEDIAMESSAGES_H_ -#define TALK_SESSION_PHONE_MEDIAMESSAGES_H_ - -#include -#include -#include "talk/base/basictypes.h" -#include "talk/p2p/base/parsing.h" -#include "talk/p2p/base/sessiondescription.h" - -namespace cricket { - -struct NamedSource { - NamedSource() : ssrc(0), ssrc_set(false), removed(false) {} - - void SetSsrc(uint32 ssrc) { - this->ssrc = ssrc; - this->ssrc_set = true; - } - - std::string nick; - std::string name; - std::string usage; - uint32 ssrc; - bool ssrc_set; - bool removed; -}; - -typedef std::vector NamedSources; - -class MediaSources { - public: - const NamedSource* GetAudioSourceBySsrc(uint32 ssrc); - const NamedSource* GetVideoSourceBySsrc(uint32 ssrc); - // TODO: Remove once all senders use excplict remove by ssrc. - const NamedSource* GetFirstAudioSourceByNick(const std::string& nick); - const NamedSource* GetFirstVideoSourceByNick(const std::string& nick); - void AddAudioSource(const NamedSource& source); - void AddVideoSource(const NamedSource& source); - void RemoveAudioSourceBySsrc(uint32 ssrc); - void RemoveVideoSourceBySsrc(uint32 ssrc); - NamedSources audio; - NamedSources video; -}; - -struct StaticVideoView { - StaticVideoView(uint32 ssrc, int width, int height, int framerate) - : ssrc(ssrc), - width(width), - height(height), - framerate(framerate), - preference(0) {} - - uint32 ssrc; - int width; - int height; - int framerate; - int preference; -}; - -typedef std::vector StaticVideoViews; - -struct ViewRequest { - StaticVideoViews static_video_views; -}; - -bool WriteViewRequest(const std::string& content_name, - const ViewRequest& view, - XmlElements* elems, - WriteError* error); - -bool IsSourcesNotify(const buzz::XmlElement* action_elem); -// The session_description is needed to map content_name => media type. -bool ParseSourcesNotify(const buzz::XmlElement* action_elem, - const SessionDescription* session_description, - MediaSources* sources, - ParseError* error); -} // namespace cricket - -#endif // TALK_SESSION_PHONE_MEDIAMESSAGES_H_ diff --git a/third_party_mods/libjingle/source/talk/session/phone/mediasessionclient.h b/third_party_mods/libjingle/source/talk/session/phone/mediasessionclient.h deleted file mode 100644 index 07fc25863..000000000 --- a/third_party_mods/libjingle/source/talk/session/phone/mediasessionclient.h +++ /dev/null @@ -1,289 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_SESSION_PHONE_MEDIASESSIONCLIENT_H_ -#define TALK_SESSION_PHONE_MEDIASESSIONCLIENT_H_ - -#include -#include -#include -#include -#include "talk/session/phone/call.h" -#include "talk/session/phone/channelmanager.h" -#include "talk/session/phone/cryptoparams.h" -#include "talk/base/sigslot.h" -#include "talk/base/sigslotrepeater.h" -#include "talk/base/messagequeue.h" -#include "talk/base/thread.h" -#include "talk/p2p/base/sessionmanager.h" -#include "talk/p2p/base/session.h" -#include "talk/p2p/base/sessionclient.h" -#include "talk/p2p/base/sessiondescription.h" - -namespace cricket { - -class Call; -class SessionDescription; -typedef std::vector AudioCodecs; -typedef std::vector VideoCodecs; - -// SEC_ENABLED and SEC_REQUIRED should only be used if the session -// was negotiated over TLS, to protect the inline crypto material -// exchange. -// SEC_DISABLED: No crypto in outgoing offer and answer. Fail any -// offer with crypto required. -// SEC_ENABLED: Crypto in outgoing offer and answer. Fail any offer -// with unsupported required crypto. Crypto set but not -// required in outgoing offer. -// SEC_REQUIRED: Crypto in outgoing offer and answer with -// required='true'. Fail any offer with no or -// unsupported crypto (implicit crypto required='true' -// in the offer.) -enum SecureMediaPolicy {SEC_DISABLED, SEC_ENABLED, SEC_REQUIRED}; - -const int kAutoBandwidth = -1; - -struct CallOptions { - CallOptions() : - is_video(false), - is_muc(false), - video_bandwidth(kAutoBandwidth) { - } - - bool is_video; - bool is_muc; - // bps. -1 == auto. - int video_bandwidth; -}; - -class MediaSessionClient: public SessionClient, public sigslot::has_slots<> { - public: - - MediaSessionClient(const buzz::Jid& jid, SessionManager *manager); - // Alternative constructor, allowing injection of media_engine - // and device_manager. - MediaSessionClient(const buzz::Jid& jid, SessionManager *manager, - MediaEngine* media_engine, DeviceManager* device_manager); - ~MediaSessionClient(); - - const buzz::Jid &jid() const { return jid_; } - SessionManager* session_manager() const { return session_manager_; } - ChannelManager* channel_manager() const { return channel_manager_; } - - int GetCapabilities() { return channel_manager_->GetCapabilities(); } - - Call *CreateCall(); - void DestroyCall(Call *call); - - Call *GetFocus(); - void SetFocus(Call *call); - - void JoinCalls(Call *call_to_join, Call *call); - - bool GetAudioInputDevices(std::vector* names) { - return channel_manager_->GetAudioInputDevices(names); - } - bool GetAudioOutputDevices(std::vector* names) { - return channel_manager_->GetAudioOutputDevices(names); - } - bool GetVideoCaptureDevices(std::vector* names) { - return channel_manager_->GetVideoCaptureDevices(names); - } - - bool SetAudioOptions(const std::string& in_name, const std::string& out_name, - int opts) { - return channel_manager_->SetAudioOptions(in_name, out_name, opts); - } - bool SetOutputVolume(int level) { - return channel_manager_->SetOutputVolume(level); - } - bool SetVideoOptions(const std::string& cam_device) { - return channel_manager_->SetVideoOptions(cam_device); - } - - sigslot::signal2 SignalFocus; - sigslot::signal1 SignalCallCreate; - sigslot::signal1 SignalCallDestroy; - sigslot::repeater0<> SignalDevicesChange; - - SessionDescription* CreateOffer(const CallOptions& options); - SessionDescription* CreateAnswer(const SessionDescription* offer, - const CallOptions& options); - - SecureMediaPolicy secure() const { return secure_; } - void set_secure(SecureMediaPolicy s) { secure_ = s; } - - private: - void Construct(); - void OnSessionCreate(Session *session, bool received_initiate); - void OnSessionState(BaseSession *session, BaseSession::State state); - void OnSessionDestroy(Session *session); - virtual bool ParseContent(SignalingProtocol protocol, - const buzz::XmlElement* elem, - const ContentDescription** content, - ParseError* error); - virtual bool WriteContent(SignalingProtocol protocol, - const ContentDescription* content, - buzz::XmlElement** elem, - WriteError* error); - Session *CreateSession(Call *call); - - buzz::Jid jid_; - SessionManager* session_manager_; - Call *focus_call_; - ChannelManager *channel_manager_; - std::map calls_; - std::map session_map_; - SecureMediaPolicy secure_; - friend class Call; -}; - -enum MediaType { - MEDIA_TYPE_AUDIO, - MEDIA_TYPE_VIDEO -}; - -class MediaContentDescription : public ContentDescription { - public: - MediaContentDescription() - : ssrc_(0), - ssrc_set_(false), - rtcp_mux_(false), - bandwidth_(kAutoBandwidth), - crypto_required_(false), - rtp_header_extensions_set_(false) { - } - - virtual MediaType type() const = 0; - - uint32 ssrc() const { return ssrc_; } - bool ssrc_set() const { return ssrc_set_; } - void set_ssrc(uint32 ssrc) { - ssrc_ = ssrc; - ssrc_set_ = true; - } - - bool rtcp_mux() const { return rtcp_mux_; } - void set_rtcp_mux(bool mux) { rtcp_mux_ = mux; } - - int bandwidth() const { return bandwidth_; } - void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; } - - const std::vector& cryptos() const { return cryptos_; } - void AddCrypto(const CryptoParams& params) { - cryptos_.push_back(params); - } - bool crypto_required() const { return crypto_required_; } - void set_crypto_required(bool crypto) { - crypto_required_ = crypto; - } - - const std::vector& rtp_header_extensions() const { - return rtp_header_extensions_; - } - void AddRtpHeaderExtension(const RtpHeaderExtension& ext) { - rtp_header_extensions_.push_back(ext); - rtp_header_extensions_set_ = true; - } - void ClearRtpHeaderExtensions() { - rtp_header_extensions_.clear(); - rtp_header_extensions_set_ = true; - } - // We can't always tell if an empty list of header extensions is - // because the other side doesn't support them, or just isn't hooked up to - // signal them. For now we assume an empty list means no signaling, but - // provide the ClearRtpHeaderExtensions method to allow "no support" to be - // clearly indicated (i.e. when derived from other information). - bool rtp_header_extensions_set() const { - return rtp_header_extensions_set_; - } - - protected: - uint32 ssrc_; - bool ssrc_set_; - bool rtcp_mux_; - int bandwidth_; - std::vector cryptos_; - bool crypto_required_; - std::vector rtp_header_extensions_; - bool rtp_header_extensions_set_; -}; - -template -class MediaContentDescriptionImpl : public MediaContentDescription { - public: - struct PreferenceSort { - bool operator()(C a, C b) { return a.preference > b.preference; } - }; - - const std::vector& codecs() const { return codecs_; } - void AddCodec(const C& codec) { - codecs_.push_back(codec); - } - void SortCodecs() { - std::sort(codecs_.begin(), codecs_.end(), PreferenceSort()); - } - - private: - std::vector codecs_; -}; - -class AudioContentDescription : public MediaContentDescriptionImpl { - public: - AudioContentDescription() : - conference_mode_(false) {} - - virtual MediaType type() const { return MEDIA_TYPE_AUDIO; } - - bool conference_mode() const { return conference_mode_; } - void set_conference_mode(bool enable) { - conference_mode_ = enable; - } - - const std::string &lang() const { return lang_; } - void set_lang(const std::string &lang) { lang_ = lang; } - - - private: - bool conference_mode_; - std::string lang_; -}; - -class VideoContentDescription : public MediaContentDescriptionImpl { - public: - virtual MediaType type() const { return MEDIA_TYPE_VIDEO; } -}; - -// Convenience functions. -bool IsAudioContent(const ContentInfo* content); -bool IsVideoContent(const ContentInfo* content); -const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc); -const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc); - -} // namespace cricket - -#endif // TALK_SESSION_PHONE_MEDIASESSIONCLIENT_H_ diff --git a/third_party_mods/libjingle/source/talk/session/phone/srtpfilter.cc b/third_party_mods/libjingle/source/talk/session/phone/srtpfilter.cc new file mode 100644 index 000000000..2119890d2 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/srtpfilter.cc @@ -0,0 +1,660 @@ +/* + * libjingle + * Copyright 2009, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// talk's config.h, generated from mac_config_dot_h for OSX, conflicts with the +// one included by the libsrtp headers. Don't use it. Instead, we keep HAVE_SRTP +// and LOGGING defined in config.h. +#undef HAVE_CONFIG_H + +#ifdef OSX +// TODO: For the XCode build, we force SRTP (b/2500074) +#ifndef HAVE_SRTP +#define HAVE_SRTP 1 +#endif // HAVE_SRTP +// If LOGGING is not defined, define it to 1 (b/3245816) +#ifndef LOGGING +#define LOGGING 1 +#endif // HAVE_SRTP +#endif + +#include "talk/session/phone/srtpfilter.h" + +#include +#include + +#include "talk/base/base64.h" +#include "talk/base/logging.h" +#include "talk/base/time.h" +#include "talk/session/phone/rtputils.h" + +// Enable this line to turn on SRTP debugging +// #define SRTP_DEBUG + +#ifdef HAVE_SRTP +#ifdef SRTP_RELATIVE_PATH +#include "srtp.h" // NOLINT +#else +#include "third_party/libsrtp/include/srtp.h" +#endif // SRTP_RELATIVE_PATH +#ifdef _DEBUG +extern "C" debug_module_t mod_srtp; +extern "C" debug_module_t mod_auth; +extern "C" debug_module_t mod_cipher; +extern "C" debug_module_t mod_stat; +extern "C" debug_module_t mod_alloc; +extern "C" debug_module_t mod_aes_icm; +extern "C" debug_module_t mod_aes_hmac; +#endif +#else +// SrtpFilter needs that constant. +#define SRTP_MASTER_KEY_LEN 30 +#endif // HAVE_SRTP + +namespace cricket { + +const std::string& CS_DEFAULT = CS_AES_CM_128_HMAC_SHA1_80; +const std::string CS_AES_CM_128_HMAC_SHA1_80 = "AES_CM_128_HMAC_SHA1_80"; +const std::string CS_AES_CM_128_HMAC_SHA1_32 = "AES_CM_128_HMAC_SHA1_32"; +const int SRTP_MASTER_KEY_BASE64_LEN = SRTP_MASTER_KEY_LEN * 4 / 3; + +#ifndef HAVE_SRTP + +// This helper function is used on systems that don't (yet) have SRTP, +// to log that the functions that require it won't do anything. +namespace { +bool SrtpNotAvailable(const char *func) { + LOG(LS_ERROR) << func << ": SRTP is not available on your system."; + return false; +} +} // anonymous namespace + +#endif // !HAVE_SRTP + +#ifdef HAVE_SRTP //due to cricket namespace it can't be clubbed with above cond +void EnableSrtpDebugging() { +#ifdef _DEBUG + debug_on(mod_srtp); + debug_on(mod_auth); + debug_on(mod_cipher); + debug_on(mod_stat); + debug_on(mod_alloc); + debug_on(mod_aes_icm); + // debug_on(mod_aes_cbc); + // debug_on(mod_hmac); +#endif +} +#endif + +SrtpFilter::SrtpFilter() + : state_(ST_INIT), + send_session_(new SrtpSession()), + recv_session_(new SrtpSession()) { + SignalSrtpError.repeat(send_session_->SignalSrtpError); + SignalSrtpError.repeat(recv_session_->SignalSrtpError); +} + +SrtpFilter::~SrtpFilter() { +} + +bool SrtpFilter::IsActive() const { + return (state_ == ST_ACTIVE); +} + +bool SrtpFilter::SetOffer(const std::vector& offer_params, + ContentSource source) { + bool ret = false; + if (state_ == ST_INIT) { + ret = StoreParams(offer_params, source); + } else { + LOG(LS_ERROR) << "Invalid state for SRTP offer"; + } + return ret; +} + +bool SrtpFilter::SetAnswer(const std::vector& answer_params, + ContentSource source) { + bool ret = false; + if ((state_ == ST_SENTOFFER && source == CS_REMOTE) || + (state_ == ST_RECEIVEDOFFER && source == CS_LOCAL)) { + // If the answer requests crypto, finalize the parameters and apply them. + // Otherwise, complete the negotiation of a unencrypted session. + if (!answer_params.empty()) { + CryptoParams selected_params; + ret = NegotiateParams(answer_params, &selected_params); + if (ret) { + if (state_ == ST_SENTOFFER) { + ret = ApplyParams(selected_params, answer_params[0]); + } else { // ST_RECEIVEDOFFER + ret = ApplyParams(answer_params[0], selected_params); + } + } + } else { + ret = ResetParams(); + } + } else { + LOG(LS_ERROR) << "Invalid state for SRTP answer"; + } + return ret; +} + +bool SrtpFilter::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { + if (!IsActive()) { + LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active"; + return false; + } + return send_session_->ProtectRtp(p, in_len, max_len, out_len); +} + +bool SrtpFilter::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { + if (!IsActive()) { + LOG(LS_WARNING) << "Failed to ProtectRtcp: SRTP not active"; + return false; + } + return send_session_->ProtectRtcp(p, in_len, max_len, out_len); +} + +bool SrtpFilter::UnprotectRtp(void* p, int in_len, int* out_len) { + if (!IsActive()) { + LOG(LS_WARNING) << "Failed to UnprotectRtp: SRTP not active"; + return false; + } + return recv_session_->UnprotectRtp(p, in_len, out_len); +} + +bool SrtpFilter::UnprotectRtcp(void* p, int in_len, int* out_len) { + if (!IsActive()) { + LOG(LS_WARNING) << "Failed to UnprotectRtcp: SRTP not active"; + return false; + } + return recv_session_->UnprotectRtcp(p, in_len, out_len); +} + +void SrtpFilter::set_signal_silent_time(uint32 signal_silent_time_in_ms) { + send_session_->set_signal_silent_time(signal_silent_time_in_ms); + recv_session_->set_signal_silent_time(signal_silent_time_in_ms); +} + +bool SrtpFilter::StoreParams(const std::vector& params, + ContentSource source) { + offer_params_ = params; + state_ = (source == CS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER; + return true; +} + +bool SrtpFilter::NegotiateParams(const std::vector& answer_params, + CryptoParams* selected_params) { + // We're processing an accept. We should have exactly one set of params, + // unless the offer didn't mention crypto, in which case we shouldn't be here. + bool ret = (answer_params.size() == 1U && !offer_params_.empty()); + if (ret) { + // We should find a match between the answer params and the offered params. + std::vector::const_iterator it; + for (it = offer_params_.begin(); it != offer_params_.end(); ++it) { + if (answer_params[0].Matches(*it)) { + break; + } + } + + if (it != offer_params_.end()) { + *selected_params = *it; + } else { + ret = false; + } + } + + if (!ret) { + LOG(LS_WARNING) << "Invalid parameters in SRTP answer"; + } + return ret; +} + +bool SrtpFilter::ApplyParams(const CryptoParams& send_params, + const CryptoParams& recv_params) { + // TODO: Zero these buffers after use. + bool ret; + uint8 send_key[SRTP_MASTER_KEY_LEN], recv_key[SRTP_MASTER_KEY_LEN]; + ret = (ParseKeyParams(send_params.key_params, send_key, sizeof(send_key)) && + ParseKeyParams(recv_params.key_params, recv_key, sizeof(recv_key))); + if (ret) { + ret = (send_session_->SetSend(send_params.cipher_suite, + send_key, sizeof(send_key)) && + recv_session_->SetRecv(recv_params.cipher_suite, + recv_key, sizeof(recv_key))); + } + if (ret) { + offer_params_.clear(); + state_ = ST_ACTIVE; + LOG(LS_INFO) << "SRTP activated with negotiated parameters:" + << " send cipher_suite " << send_params.cipher_suite + << " recv cipher_suite " << recv_params.cipher_suite; + } else { + LOG(LS_WARNING) << "Failed to apply negotiated SRTP parameters"; + } + return ret; +} + +bool SrtpFilter::ResetParams() { + offer_params_.clear(); + state_ = ST_INIT; + LOG(LS_INFO) << "SRTP reset to init state"; + return true; +} + +bool SrtpFilter::ParseKeyParams(const std::string& key_params, + uint8* key, int len) { + // example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2" + + // Fail if key-method is wrong. + if (key_params.find("inline:") != 0) { + return false; + } + + // Fail if base64 decode fails, or the key is the wrong size. + std::string key_b64(key_params.substr(7)), key_str; + if (!talk_base::Base64::Decode(key_b64, talk_base::Base64::DO_STRICT, + &key_str, NULL) || + static_cast(key_str.size()) != len) { + return false; + } + + memcpy(key, key_str.c_str(), len); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// SrtpSession + +#ifdef HAVE_SRTP + +bool SrtpSession::inited_ = false; +std::list SrtpSession::sessions_; + +SrtpSession::SrtpSession() + : session_(NULL), + rtp_auth_tag_len_(0), + rtcp_auth_tag_len_(0), + srtp_stat_(new SrtpStat()), + last_send_seq_num_(-1) { + sessions_.push_back(this); + SignalSrtpError.repeat(srtp_stat_->SignalSrtpError); +} + +SrtpSession::~SrtpSession() { + sessions_.erase(std::find(sessions_.begin(), sessions_.end(), this)); + if (session_) { + srtp_dealloc(session_); + } +} + +bool SrtpSession::SetSend(const std::string& cs, const uint8* key, int len) { + return SetKey(ssrc_any_outbound, cs, key, len); +} + +bool SrtpSession::SetRecv(const std::string& cs, const uint8* key, int len) { + return SetKey(ssrc_any_inbound, cs, key, len); +} + +bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { + if (!session_) { + LOG(LS_WARNING) << "Failed to protect SRTP packet: no SRTP Session"; + return false; + } + + int need_len = in_len + rtp_auth_tag_len_; // NOLINT + if (max_len < need_len) { + LOG(LS_WARNING) << "Failed to protect SRTP packet: The buffer length " + << max_len << " is less than the needed " << need_len; + return false; + } + + *out_len = in_len; + int err = srtp_protect(session_, p, out_len); + uint32 ssrc; + if (GetRtpSsrc(p, in_len, &ssrc)) { + srtp_stat_->AddProtectRtpResult(ssrc, err); + } + int seq_num; + GetRtpSeqNum(p, in_len, &seq_num); + if (err != err_status_ok) { + LOG(LS_WARNING) << "Failed to protect SRTP packet, seqnum=" + << seq_num << ", err=" << err << ", last seqnum=" + << last_send_seq_num_; + return false; + } + last_send_seq_num_ = seq_num; + return true; +} + +bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { + if (!session_) { + LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session"; + return false; + } + + int need_len = in_len + sizeof(uint32) + rtcp_auth_tag_len_; // NOLINT + if (max_len < need_len) { + LOG(LS_WARNING) << "Failed to protect SRTCP packet: The buffer length " + << max_len << " is less than the needed " << need_len; + return false; + } + + *out_len = in_len; + int err = srtp_protect_rtcp(session_, p, out_len); + srtp_stat_->AddProtectRtcpResult(err); + if (err != err_status_ok) { + LOG(LS_WARNING) << "Failed to protect SRTCP packet, err=" << err; + return false; + } + return true; +} + +bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) { + if (!session_) { + LOG(LS_WARNING) << "Failed to unprotect SRTP packet: no SRTP Session"; + return false; + } + + *out_len = in_len; + int err = srtp_unprotect(session_, p, out_len); + uint32 ssrc; + if (GetRtpSsrc(p, in_len, &ssrc)) { + srtp_stat_->AddUnprotectRtpResult(ssrc, err); + } + if (err != err_status_ok) { + LOG(LS_WARNING) << "Failed to unprotect SRTP packet, err=" << err; + return false; + } + return true; +} + +bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) { + if (!session_) { + LOG(LS_WARNING) << "Failed to unprotect SRTCP packet: no SRTP Session"; + return false; + } + + *out_len = in_len; + int err = srtp_unprotect_rtcp(session_, p, out_len); + srtp_stat_->AddUnprotectRtcpResult(err); + if (err != err_status_ok) { + LOG(LS_WARNING) << "Failed to unprotect SRTCP packet, err=" << err; + return false; + } + return true; +} + +void SrtpSession::set_signal_silent_time(uint32 signal_silent_time_in_ms) { + srtp_stat_->set_signal_silent_time(signal_silent_time_in_ms); +} + +bool SrtpSession::SetKey(int type, const std::string& cs, + const uint8* key, int len) { + if (session_) { + LOG(LS_ERROR) << "Failed to create SRTP session: " + << "SRTP session already created"; + return false; + } + + if (!Init()) { + return false; + } + + srtp_policy_t policy; + memset(&policy, 0, sizeof(policy)); + + if (cs == CS_AES_CM_128_HMAC_SHA1_80) { + crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); + crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); + } else if (cs == CS_AES_CM_128_HMAC_SHA1_32) { + crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); // rtp is 32, + crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // rtcp still 80 + } else { + LOG(LS_WARNING) << "Failed to create SRTP session: unsupported" + << " cipher_suite " << cs.c_str(); + return false; + } + + if (!key || len != SRTP_MASTER_KEY_LEN) { + LOG(LS_WARNING) << "Failed to create SRTP session: invalid key"; + return false; + } + + policy.ssrc.type = static_cast(type); + policy.ssrc.value = 0; + policy.key = const_cast(key); + // TODO parse window size from WSH session-param + policy.window_size = 1024; + policy.allow_repeat_tx = 1; + policy.next = NULL; + + int err = srtp_create(&session_, &policy); + if (err != err_status_ok) { + LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err; + return false; + } + + rtp_auth_tag_len_ = policy.rtp.auth_tag_len; + rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len; + return true; +} + +bool SrtpSession::Init() { + if (!inited_) { + int err; + err = srtp_init(); + if (err != err_status_ok) { + LOG(LS_ERROR) << "Failed to init SRTP, err=" << err; + return false; + } + + err = srtp_install_event_handler(&SrtpSession::HandleEventThunk); + if (err != err_status_ok) { + LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err; + return false; + } + + inited_ = true; + } + + return true; +} + +void SrtpSession::HandleEvent(const srtp_event_data_t* ev) { + switch (ev->event) { + case event_ssrc_collision: + LOG(LS_INFO) << "SRTP event: SSRC collision"; + break; + case event_key_soft_limit: + LOG(LS_INFO) << "SRTP event: reached soft key usage limit"; + break; + case event_key_hard_limit: + LOG(LS_INFO) << "SRTP event: reached hard key usage limit"; + break; + case event_packet_index_limit: + LOG(LS_INFO) << "SRTP event: reached hard packet limit (2^48 packets)"; + break; + default: + LOG(LS_INFO) << "SRTP event: unknown " << ev->event; + break; + } +} + +void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) { + for (std::list::iterator it = sessions_.begin(); + it != sessions_.end(); ++it) { + if ((*it)->session_ == ev->session) { + (*it)->HandleEvent(ev); + break; + } + } +} + +#else // !HAVE_SRTP + + +SrtpSession::SrtpSession() { + LOG(WARNING) << "SRTP implementation is missing."; +} + +SrtpSession::~SrtpSession() { +} + +bool SrtpSession::SetSend(const std::string& cs, const uint8* key, int len) { + return SrtpNotAvailable(__FUNCTION__); +} + +bool SrtpSession::SetRecv(const std::string& cs, const uint8* key, int len) { + return SrtpNotAvailable(__FUNCTION__); +} + +bool SrtpSession::ProtectRtp(void* data, int in_len, int max_len, + int* out_len) { + return SrtpNotAvailable(__FUNCTION__); +} + +bool SrtpSession::ProtectRtcp(void* data, int in_len, int max_len, + int* out_len) { + return SrtpNotAvailable(__FUNCTION__); +} + +bool SrtpSession::UnprotectRtp(void* data, int in_len, int* out_len) { + return SrtpNotAvailable(__FUNCTION__); +} + +bool SrtpSession::UnprotectRtcp(void* data, int in_len, int* out_len) { + return SrtpNotAvailable(__FUNCTION__); +} + +void SrtpSession::set_signal_silent_time(uint32 signal_silent_time) { + // Do nothing. +} + +#endif // HAVE_SRTP + +/////////////////////////////////////////////////////////////////////////////// +// SrtpStat + +#ifdef HAVE_SRTP + +SrtpStat::SrtpStat() + : signal_silent_time_(1000) { +} + +void SrtpStat::AddProtectRtpResult(uint32 ssrc, int result) { + FailureKey key; + key.ssrc = ssrc; + key.mode = SrtpFilter::PROTECT; + switch (result) { + case err_status_ok: + key.error = SrtpFilter::ERROR_NONE; + break; + case err_status_auth_fail: + key.error = SrtpFilter::ERROR_AUTH; + break; + default: + key.error = SrtpFilter::ERROR_FAIL; + } + HandleSrtpResult(key); +} + +void SrtpStat::AddUnprotectRtpResult(uint32 ssrc, int result) { + FailureKey key; + key.ssrc = ssrc; + key.mode = SrtpFilter::UNPROTECT; + switch (result) { + case err_status_ok: + key.error = SrtpFilter::ERROR_NONE; + break; + case err_status_auth_fail: + key.error = SrtpFilter::ERROR_AUTH; + break; + case err_status_replay_fail: + case err_status_replay_old: + key.error = SrtpFilter::ERROR_REPLAY; + break; + default: + key.error = SrtpFilter::ERROR_FAIL; + } + HandleSrtpResult(key); +} + +void SrtpStat::AddProtectRtcpResult(int result) { + AddProtectRtpResult(0U, result); +} + +void SrtpStat::AddUnprotectRtcpResult(int result) { + AddUnprotectRtpResult(0U, result); +} + +void SrtpStat::HandleSrtpResult(const SrtpStat::FailureKey& key) { + // Handle some cases where error should be signalled right away. For other + // errors, trigger error for the first time seeing it. After that, silent + // the same error for a certain amount of time (default 1 sec). + if (key.error != SrtpFilter::ERROR_NONE) { + // For errors, signal first time and wait for 1 sec. + FailureStat* stat = &(failures_[key]); + uint32 current_time = talk_base::Time(); + if (stat->last_signal_time == 0 || + talk_base::TimeDiff(current_time, stat->last_signal_time) > + static_cast(signal_silent_time_)) { + SignalSrtpError(key.ssrc, key.mode, key.error); + stat->last_signal_time = current_time; + } + } +} + +#else // !HAVE_SRTP + + +SrtpStat::SrtpStat() + : signal_silent_time_(1000) { + LOG(WARNING) << "SRTP implementation is missing."; +} + +void SrtpStat::AddProtectRtpResult(uint32 ssrc, int result) { + SrtpNotAvailable(__FUNCTION__); +} + +void SrtpStat::AddUnprotectRtpResult(uint32 ssrc, int result) { + SrtpNotAvailable(__FUNCTION__); +} + +void SrtpStat::AddProtectRtcpResult(int result) { + SrtpNotAvailable(__FUNCTION__); +} + +void SrtpStat::AddUnprotectRtcpResult(int result) { + SrtpNotAvailable(__FUNCTION__); +} + +void SrtpStat::HandleSrtpResult(const SrtpStat::FailureKey& key) { + SrtpNotAvailable(__FUNCTION__); +} + +#endif // HAVE_SRTP + +} // namespace cricket diff --git a/third_party_mods/libjingle/source/talk/session/phone/webrtccommon.h b/third_party_mods/libjingle/source/talk/session/phone/webrtccommon.h new file mode 100644 index 000000000..60fee2740 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/webrtccommon.h @@ -0,0 +1,84 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef TALK_SESSION_PHONE_WEBRTCCOMMON_H_ +#define TALK_SESSION_PHONE_WEBRTCCOMMON_H_ + +#ifdef WEBRTC_RELATIVE_PATH +#include "common_types.h" +#include "video_engine/main/interface/vie_base.h" +#include "voice_engine/main/interface/voe_base.h" +#else +#include "third_party/webrtc/files/include/common_types.h" +#include "third_party/webrtc/files/include/voe_base.h" +#include "third_party/webrtc/files/include/vie_base.h" +#endif // WEBRTC_RELATIVE_PATH + +namespace cricket { + +// Tracing helpers, for easy logging when WebRTC calls fail. +// Example: "LOG_RTCERR1(StartSend, channel);" produces the trace +// "StartSend(1) failed, err=XXXX" +// The method GetLastEngineError must be defined in the calling scope. +#define LOG_RTCERR0(func) \ + LOG_RTCERR0_EX(func, GetLastEngineError()) +#define LOG_RTCERR1(func, a1) \ + LOG_RTCERR1_EX(func, a1, GetLastEngineError()) +#define LOG_RTCERR2(func, a1, a2) \ + LOG_RTCERR2_EX(func, a1, a2, GetLastEngineError()) +#define LOG_RTCERR3(func, a1, a2, a3) \ + LOG_RTCERR3_EX(func, a1, a2, a3, GetLastEngineError()) +#define LOG_RTCERR4(func, a1, a2, a3, a4) \ + LOG_RTCERR4_EX(func, a1, a2, a3, a4, GetLastEngineError()) +#define LOG_RTCERR5(func, a1, a2, a3, a4, a5) \ + LOG_RTCERR5_EX(func, a1, a2, a3, a4, a5, GetLastEngineError()) +#define LOG_RTCERR6(func, a1, a2, a3, a4, a5, a6) \ + LOG_RTCERR6_EX(func, a1, a2, a3, a4, a5, a6, GetLastEngineError()) +#define LOG_RTCERR0_EX(func, err) LOG(LS_WARNING) \ + << "" << #func << "() failed, err=" << err +#define LOG_RTCERR1_EX(func, a1, err) LOG(LS_WARNING) \ + << "" << #func << "(" << a1 << ") failed, err=" << err +#define LOG_RTCERR2_EX(func, a1, a2, err) LOG(LS_WARNING) \ + << "" << #func << "(" << a1 << ", " << a2 << ") failed, err=" \ + << err +#define LOG_RTCERR3_EX(func, a1, a2, a3, err) LOG(LS_WARNING) \ + << "" << #func << "(" << a1 << ", " << a2 << ", " << a3 \ + << ") failed, err=" << err +#define LOG_RTCERR4_EX(func, a1, a2, a3, a4, err) LOG(LS_WARNING) \ + << "" << #func << "(" << a1 << ", " << a2 << ", " << a3 \ + << ", " << a4 << ") failed, err=" << err +#define LOG_RTCERR5_EX(func, a1, a2, a3, a4, a5, err) LOG(LS_WARNING) \ + << "" << #func << "(" << a1 << ", " << a2 << ", " << a3 \ + << ", " << a4 << ", " << a5 << ") failed, err=" << err +#define LOG_RTCERR6_EX(func, a1, a2, a3, a4, a5, a6, err) LOG(LS_WARNING) \ + << "" << #func << "(" << a1 << ", " << a2 << ", " << a3 \ + << ", " << a4 << ", " << a5 << ", " << a6 << ") failed, err=" << err + +} // namespace cricket + +#endif // TALK_SESSION_PHONE_WEBRTCCOMMON_H_ diff --git a/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoengine.cc b/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoengine.cc new file mode 100644 index 000000000..63fc8cd01 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoengine.cc @@ -0,0 +1,916 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_WEBRTC + +#include "talk/session/phone/webrtcvideoengine.h" + +#include "talk/base/common.h" +#include "talk/base/buffer.h" +#include "talk/base/byteorder.h" +#include "talk/base/logging.h" +#include "talk/base/stringutils.h" +#include "talk/session/phone/webrtcvoiceengine.h" +#include "talk/session/phone/webrtcvideoframe.h" +#include "talk/session/phone/webrtcvie.h" +#include "talk/session/phone/webrtcvoe.h" + +namespace cricket { + +static const int kDefaultLogSeverity = talk_base::LS_WARNING; +static const int kStartVideoBitrate = 300; +static const int kMaxVideoBitrate = 1000; + +class WebRtcRenderAdapter : public webrtc::ExternalRenderer { + public: + explicit WebRtcRenderAdapter(VideoRenderer* renderer) + : renderer_(renderer) { + } + + virtual int FrameSizeChange(unsigned int width, unsigned int height, + unsigned int /*number_of_streams*/) { + ASSERT(renderer_ != NULL); + width_ = width; + height_ = height; + return renderer_->SetSize(width_, height_, 0) ? 0 : -1; + } + + virtual int DeliverFrame(unsigned char* buffer, int buffer_size) { + ASSERT(renderer_ != NULL); + WebRtcVideoFrame video_frame; + // TODO(ronghuawu): Currently by the time DeliverFrame got called, + // ViE expects the frame will be rendered ASAP. However, the libjingle + // renderer may have its own internal delays. Can you disable the buffering + // inside ViE and surface the timing information to this callback? + video_frame.Attach(buffer, buffer_size, width_, height_, 0, 0); + int ret = renderer_->RenderFrame(&video_frame) ? 0 : -1; + uint8* buffer_temp; + size_t buffer_size_temp; + video_frame.Detach(&buffer_temp, &buffer_size_temp); + return ret; + } + + virtual ~WebRtcRenderAdapter() {} + + private: + VideoRenderer* renderer_; + unsigned int width_; + unsigned int height_; +}; + +const WebRtcVideoEngine::VideoCodecPref + WebRtcVideoEngine::kVideoCodecPrefs[] = { + {"VP8", 104, 0}, + {"H264", 105, 1} +}; + +WebRtcVideoEngine::WebRtcVideoEngine() + : vie_wrapper_(new ViEWrapper()), + capture_(NULL), + external_capture_(false), + capture_id_(-1), + renderer_(webrtc::VideoRender::CreateVideoRender(0, NULL, + false, webrtc::kRenderExternal)), + voice_engine_(NULL), + log_level_(kDefaultLogSeverity), + capture_started_(false) { +} + +WebRtcVideoEngine::WebRtcVideoEngine(WebRtcVoiceEngine* voice_engine, + webrtc::VideoCaptureModule* capture) + : vie_wrapper_(new ViEWrapper()), + capture_(capture), + external_capture_(true), + capture_id_(-1), + renderer_(webrtc::VideoRender::CreateVideoRender(0, NULL, + false, webrtc::kRenderExternal)), + voice_engine_(voice_engine), + log_level_(kDefaultLogSeverity), + capture_started_(false) { +} + +WebRtcVideoEngine::WebRtcVideoEngine(WebRtcVoiceEngine* voice_engine, + ViEWrapper* vie_wrapper) + : vie_wrapper_(vie_wrapper), + capture_(NULL), + external_capture_(false), + capture_id_(-1), + renderer_(webrtc::VideoRender::CreateVideoRender(0, NULL, + false, webrtc::kRenderExternal)), + voice_engine_(voice_engine), + log_level_(kDefaultLogSeverity), + capture_started_(false) { +} + +WebRtcVideoEngine::~WebRtcVideoEngine() { + LOG(LS_INFO) << " WebRtcVideoEngine::~WebRtcVideoEngine"; + vie_wrapper_->engine()->SetTraceCallback(NULL); + Terminate(); + vie_wrapper_.reset(); + if (capture_) { + webrtc::VideoCaptureModule::Destroy(capture_); + } + if (renderer_) { + webrtc::VideoRender::DestroyVideoRender(renderer_); + } +} + +bool WebRtcVideoEngine::Init() { + LOG(LS_INFO) << "WebRtcVideoEngine::Init"; + ApplyLogging(); + if (vie_wrapper_->engine()->SetTraceCallback(this) != 0) { + LOG_RTCERR1(SetTraceCallback, this); + } + + bool result = InitVideoEngine(); + if (result) { + LOG(LS_INFO) << "VideoEngine Init done"; + } else { + LOG(LS_ERROR) << "VideoEngine Init failed, releasing"; + Terminate(); + } + return result; +} + +bool WebRtcVideoEngine::InitVideoEngine() { + LOG(LS_INFO) << "WebRtcVideoEngine::InitVideoEngine"; + + if (vie_wrapper_->base()->Init() != 0) { + LOG_RTCERR0(Init); + return false; + } + + if (!voice_engine_) { + LOG(LS_WARNING) << "NULL voice engine"; + } else if ((vie_wrapper_->base()->SetVoiceEngine( + voice_engine_->voe()->engine())) != 0) { + LOG_RTCERR0(SetVoiceEngine); + return false; + } + + if ((vie_wrapper_->base()->RegisterObserver(*this)) != 0) { + LOG_RTCERR0(RegisterObserver); + return false; + } + + int ncodecs = vie_wrapper_->codec()->NumberOfCodecs(); + for (int i = 0; i < ncodecs; ++i) { + webrtc::VideoCodec wcodec; + if ((vie_wrapper_->codec()->GetCodec(i, wcodec) == 0) && + (strncmp(wcodec.plName, "I420", 4) != 0) && + (strncmp(wcodec.plName, "ULPFEC", 4) != 0) && + (strncmp(wcodec.plName, "RED", 4) != 0)) { + // ignore I420, FEC(RED and ULPFEC) + VideoCodec codec(wcodec.plType, wcodec.plName, wcodec.width, + wcodec.height, wcodec.maxFramerate, i); + LOG(LS_INFO) << codec.ToString(); + video_codecs_.push_back(codec); + } + } + + if (vie_wrapper_->render()->RegisterVideoRenderModule(*renderer_) != 0) { + LOG_RTCERR0(RegisterVideoRenderModule); + return false; + } + + std::sort(video_codecs_.begin(), video_codecs_.end(), + &VideoCodec::Preferable); + return true; +} + +void WebRtcVideoEngine::PerformanceAlarm(const unsigned int cpu_load) { + LOG(LS_INFO) << "WebRtcVideoEngine::PerformanceAlarm"; +} + +// Ignore spammy trace messages, mostly from the stats API when we haven't +// gotten RTCP info yet from the remote side. +static bool ShouldIgnoreTrace(const std::string& trace) { + static const char* kTracesToIgnore[] = { + "\tfailed to GetReportBlockInformation", + NULL + }; + for (const char* const* p = kTracesToIgnore; *p; ++p) { + if (trace.find(*p) == 0) { + return true; + } + } + return false; +} + +void WebRtcVideoEngine::Print(const webrtc::TraceLevel level, + const char* trace, const int length) { + talk_base::LoggingSeverity sev = talk_base::LS_VERBOSE; + if (level == webrtc::kTraceError || level == webrtc::kTraceCritical) + sev = talk_base::LS_ERROR; + else if (level == webrtc::kTraceWarning) + sev = talk_base::LS_WARNING; + else if (level == webrtc::kTraceStateInfo || level == webrtc::kTraceInfo) + sev = talk_base::LS_INFO; + + if (sev >= log_level_) { + // Skip past boilerplate prefix text + if (length < 72) { + std::string msg(trace, length); + LOG(LS_ERROR) << "Malformed webrtc log message: "; + LOG_V(sev) << msg; + } else { + std::string msg(trace + 71, length - 72); + if (!ShouldIgnoreTrace(msg)) { + LOG_V(sev) << "WebRtc ViE:" << msg; + } + } + } +} + +int WebRtcVideoEngine::GetCodecPreference(const char* name) { + for (size_t i = 0; i < ARRAY_SIZE(kVideoCodecPrefs); ++i) { + if (strcmp(kVideoCodecPrefs[i].payload_name, name) == 0) { + return kVideoCodecPrefs[i].pref; + } + } + return -1; +} + +void WebRtcVideoEngine::ApplyLogging() { + int filter = 0; + switch (log_level_) { + case talk_base::LS_VERBOSE: filter |= webrtc::kTraceAll; + case talk_base::LS_INFO: filter |= webrtc::kTraceStateInfo; + case talk_base::LS_WARNING: filter |= webrtc::kTraceWarning; + case talk_base::LS_ERROR: filter |= + webrtc::kTraceError | webrtc::kTraceCritical; + } +} + +void WebRtcVideoEngine::Terminate() { + LOG(LS_INFO) << "WebRtcVideoEngine::Terminate"; + SetCapture(false); + if (local_renderer_.get()) { + // If the renderer already set, stop it first + if (vie_wrapper_->render()->StopRender(capture_id_) != 0) + LOG_RTCERR1(StopRender, capture_id_); + } + + if (vie_wrapper_->render()->DeRegisterVideoRenderModule(*renderer_) != 0) + LOG_RTCERR0(DeRegisterVideoRenderModule); + + if ((vie_wrapper_->base()->DeregisterObserver()) != 0) + LOG_RTCERR0(DeregisterObserver); + + if ((vie_wrapper_->base()->SetVoiceEngine(NULL)) != 0) + LOG_RTCERR0(SetVoiceEngine); + + if (vie_wrapper_->engine()->SetTraceCallback(NULL) != 0) + LOG_RTCERR0(SetTraceCallback); +} + +int WebRtcVideoEngine::GetCapabilities() { + return MediaEngine::VIDEO_RECV | MediaEngine::VIDEO_SEND; +} + +bool WebRtcVideoEngine::SetOptions(int options) { + return true; +} + +bool WebRtcVideoEngine::ReleaseCaptureDevice() { + if (capture_id_ != -1) { + // Stop capture + SetCapture(false); + // DisconnectCaptureDevice + WebRtcVideoMediaChannel* channel; + for (VideoChannels::const_iterator it = channels_.begin(); + it != channels_.end(); ++it) { + ASSERT(*it != NULL); + channel = *it; + vie_wrapper_->capture()->DisconnectCaptureDevice( + channel->video_channel()); + } + // ReleaseCaptureDevice + vie_wrapper_->capture()->ReleaseCaptureDevice(capture_id_); + capture_id_ = -1; + } + + return true; +} + +bool WebRtcVideoEngine::SetCaptureDevice(const Device* cam) { + ASSERT(vie_wrapper_.get()); + ASSERT(cam != NULL); + + ReleaseCaptureDevice(); + + webrtc::ViECapture* vie_capture = vie_wrapper_->capture(); + + // There's an external VCM + if (capture_) { + if (vie_capture->AllocateCaptureDevice(*capture_, capture_id_) != 0) + ASSERT(capture_id_ == -1); + } else if (!external_capture_) { + const unsigned int KMaxDeviceNameLength = 128; + const unsigned int KMaxUniqueIdLength = 256; + char device_name[KMaxDeviceNameLength]; + char device_id[KMaxUniqueIdLength]; + bool found = false; + for (int i = 0; i < vie_capture->NumberOfCaptureDevices(); ++i) { + memset(device_name, 0, KMaxDeviceNameLength); + memset(device_id, 0, KMaxUniqueIdLength); + if (vie_capture->GetCaptureDevice(i, device_name, KMaxDeviceNameLength, + device_id, KMaxUniqueIdLength) == 0) { + // TODO(ronghuawu): We should only compare the device_id here, + // however the devicemanager and webrtc use different format for th v4l2 + // device id. So here we also compare the device_name for now. + // For example "usb-0000:00:1d.7-6" vs "/dev/video0". + if ((cam->name.compare(reinterpret_cast(device_name)) == 0) || + (cam->id.compare(reinterpret_cast(device_id)) == 0)) { + LOG(INFO) << "Found video capture device: " << device_name; + found = true; + break; + } + } + } + if (!found) + return false; + if (vie_capture->AllocateCaptureDevice(device_id, KMaxUniqueIdLength, + capture_id_) != 0) + ASSERT(capture_id_ == -1); + } + + if (capture_id_ != -1) { + // Connect to all the channels + WebRtcVideoMediaChannel* channel; + for (VideoChannels::const_iterator it = channels_.begin(); + it != channels_.end(); ++it) { + ASSERT(*it != NULL); + channel = *it; + vie_capture->ConnectCaptureDevice(capture_id_, channel->video_channel()); + } + SetCapture(true); + } + + return (capture_id_ != -1); +} + +bool WebRtcVideoEngine::SetCaptureModule(webrtc::VideoCaptureModule* vcm) { + ReleaseCaptureDevice(); + if (capture_) { + webrtc::VideoCaptureModule::Destroy(capture_); + } + capture_ = vcm; + external_capture_ = true; + return true; +} + +bool WebRtcVideoEngine::SetLocalRenderer(VideoRenderer* renderer) { + if (local_renderer_.get()) { + // If the renderer already set, stop it first + vie_wrapper_->render()->StopRender(capture_id_); + } + local_renderer_.reset(new WebRtcRenderAdapter(renderer)); + + int ret; + ret = vie_wrapper_->render()->AddRenderer(capture_id_, + webrtc::kVideoI420, + local_renderer_.get()); + if (ret != 0) + return false; + ret = vie_wrapper_->render()->StartRender(capture_id_); + return (ret == 0); +} + +CaptureResult WebRtcVideoEngine::SetCapture(bool capture) { + if ((capture_started_ != capture) && (capture_id_ != -1)) { + int ret; + if (capture) + ret = vie_wrapper_->capture()->StartCapture(capture_id_); + else + ret = vie_wrapper_->capture()->StopCapture(capture_id_); + if (ret != 0) + return CR_NO_DEVICE; + capture_started_ = capture; + } + return CR_SUCCESS; +} + +const std::vector& WebRtcVideoEngine::codecs() const { + return video_codecs_; +} + +void WebRtcVideoEngine::SetLogging(int min_sev, const char* filter) { + log_level_ = min_sev; + ApplyLogging(); +} + +int WebRtcVideoEngine::GetLastEngineError() { + return vie_wrapper_->error(); +} + +bool WebRtcVideoEngine::SetDefaultEncoderConfig( + const VideoEncoderConfig& config) { + default_encoder_config_ = config; + return true; +} + +WebRtcVideoMediaChannel* WebRtcVideoEngine::CreateChannel( + VoiceMediaChannel* voice_channel) { + WebRtcVideoMediaChannel* channel = + new WebRtcVideoMediaChannel(this, voice_channel); + if (channel) { + if (!channel->Init()) { + delete channel; + channel = NULL; + } + } + return channel; +} + +bool WebRtcVideoEngine::FindCodec(const VideoCodec& codec) { + for (size_t i = 0; i < video_codecs_.size(); ++i) { + if (video_codecs_[i].Matches(codec)) { + return true; + } + } + return false; +} + +void WebRtcVideoEngine::ConvertToCricketVideoCodec( + const webrtc::VideoCodec& in_codec, VideoCodec& out_codec) { + out_codec.id = in_codec.plType; + out_codec.name = in_codec.plName; + out_codec.width = in_codec.width; + out_codec.height = in_codec.height; + out_codec.framerate = in_codec.maxFramerate; +} + +bool WebRtcVideoEngine::ConvertFromCricketVideoCodec( + const VideoCodec& in_codec, webrtc::VideoCodec& out_codec) { + bool found = false; + int ncodecs = vie_wrapper_->codec()->NumberOfCodecs(); + for (int i = 0; i < ncodecs; ++i) { + if ((vie_wrapper_->codec()->GetCodec(i, out_codec) == 0) && + (strncmp(out_codec.plName, + in_codec.name.c_str(), + webrtc::kPayloadNameSize - 1) == 0)) { + found = true; + break; + } + } + + if (!found) { + LOG(LS_ERROR) << "invalid codec type"; + return false; + } + + if (in_codec.id != 0) + out_codec.plType = in_codec.id; + + if (in_codec.width != 0) + out_codec.width = in_codec.width; + + if (in_codec.height != 0) + out_codec.height = in_codec.height; + + if (in_codec.framerate != 0) + out_codec.maxFramerate = in_codec.framerate; + + out_codec.maxBitrate = kMaxVideoBitrate; + out_codec.startBitrate = kStartVideoBitrate; + out_codec.minBitrate = kStartVideoBitrate; + + return true; +} + +int WebRtcVideoEngine::GetLastVideoEngineError() { + return vie_wrapper_->base()->LastError(); +} + +void WebRtcVideoEngine::RegisterChannel(WebRtcVideoMediaChannel *channel) { + channels_.push_back(channel); +} + +void WebRtcVideoEngine::UnregisterChannel(WebRtcVideoMediaChannel *channel) { + VideoChannels::iterator i = std::find(channels_.begin(), + channels_.end(), + channel); + if (i != channels_.end()) { + channels_.erase(i); + } +} + + + + +// WebRtcVideoMediaChannel + +WebRtcVideoMediaChannel::WebRtcVideoMediaChannel( + WebRtcVideoEngine* engine, VoiceMediaChannel* channel) + : engine_(engine), + voice_channel_(channel), + vie_channel_(-1), + sending_(false), + render_started_(false), + send_codec_(NULL) { + engine->RegisterChannel(this); +} + +bool WebRtcVideoMediaChannel::Init() { + bool ret = true; + if (engine_->video_engine()->base()->CreateChannel(vie_channel_) != 0) { + LOG_RTCERR1(CreateChannel, vie_channel_); + return false; + } + + LOG(LS_INFO) << "WebRtcVideoMediaChannel::Init " + << "video_channel " << vie_channel_ << " created"; + // connect audio channel + if (voice_channel_) { + WebRtcVoiceMediaChannel* channel = + static_cast (voice_channel_); + if (engine_->video_engine()->base()->ConnectAudioChannel( + vie_channel_, channel->voe_channel()) != 0) { + LOG(LS_WARNING) << "ViE ConnectAudioChannel failed" + << "A/V not synchronized"; + // Don't set ret to false; + } + } + + // Register external transport + if (engine_->video_engine()->network()->RegisterSendTransport( + vie_channel_, *this) != 0) { + ret = false; + } else { + // EnableRtcp(); // by default RTCP is disabled. + EnablePLI(); + } + return ret; +} + +WebRtcVideoMediaChannel::~WebRtcVideoMediaChannel() { + // Stop and remote renderer + SetRender(false); + if (engine()->video_engine()->render()->RemoveRenderer(vie_channel_) + == -1) { + LOG_RTCERR1(RemoveRenderer, vie_channel_); + } + + // DeRegister external transport + if (engine()->video_engine()->network()->DeregisterSendTransport( + vie_channel_) == -1) { + LOG_RTCERR1(DeregisterSendTransport, vie_channel_); + } + + // Unregister RtcChannel with the engine. + engine()->UnregisterChannel(this); + + // Delete VideoChannel + if (engine()->video_engine()->base()->DeleteChannel(vie_channel_) == -1) { + LOG_RTCERR1(DeleteChannel, vie_channel_); + } +} + +bool WebRtcVideoMediaChannel::SetRecvCodecs( + const std::vector& codecs) { + bool ret = true; + for (std::vector::const_iterator iter = codecs.begin(); + iter != codecs.end(); ++iter) { + if (engine()->FindCodec(*iter)) { + webrtc::VideoCodec wcodec; + if (engine()->ConvertFromCricketVideoCodec(*iter, wcodec)) { + if (engine()->video_engine()->codec()->SetReceiveCodec( + vie_channel_, wcodec) != 0) { + LOG_RTCERR2(SetReceiveCodec, vie_channel_, wcodec.plName); + ret = false; + } + } + } else { + LOG(LS_INFO) << "Unknown codec" << iter->name; + ret = false; + } + } + + // make channel ready to receive packets + if (ret) { + if (engine()->video_engine()->base()->StartReceive(vie_channel_) != 0) { + LOG_RTCERR1(StartReceive, vie_channel_); + ret = false; + } + } + return ret; +} + +bool WebRtcVideoMediaChannel::SetSendCodecs( + const std::vector& codecs) { + if (sending_) { + LOG(LS_ERROR) << "channel is alredy sending"; + return false; + } + + // match with local video codec list + std::vector send_codecs; + for (std::vector::const_iterator iter = codecs.begin(); + iter != codecs.end(); ++iter) { + if (engine()->FindCodec(*iter)) { + webrtc::VideoCodec wcodec; + if (engine()->ConvertFromCricketVideoCodec(*iter, wcodec)) + send_codecs.push_back(wcodec); + } + } + + // if none matches, return with set + if (send_codecs.empty()) { + LOG(LS_ERROR) << "No matching codecs avilable"; + return false; + } + + // select the first matched codec + const webrtc::VideoCodec& codec(send_codecs[0]); + send_codec_.reset(new webrtc::VideoCodec(codec)); + if (engine()->video_engine()->codec()->SetSendCodec( + vie_channel_, codec) != 0) { + LOG_RTCERR2(SetSendCodec, vie_channel_, codec.plName); + return false; + } + return true; +} + +bool WebRtcVideoMediaChannel::SetRender(bool render) { + if (render != render_started_) { + int ret; + if (render) { + ret = engine()->video_engine()->render()->StartRender(vie_channel_); + } else { + ret = engine()->video_engine()->render()->StopRender(vie_channel_); + } + if (ret != 0) { + return false; + } + render_started_ = render; + } + return true; +} + +bool WebRtcVideoMediaChannel::SetSend(bool send) { + if (send == sending()) { + return true; // no action required + } + + bool ret = true; + if (send) { // enable + if (engine()->video_engine()->base()->StartSend(vie_channel_) != 0) { + LOG_RTCERR1(StartSend, vie_channel_); + ret = false; + } + } else { // disable + if (engine()->video_engine()->base()->StopSend(vie_channel_) != 0) { + LOG_RTCERR1(StopSend, vie_channel_); + ret = false; + } + } + if (ret) + sending_ = send; + + return ret; +} + +bool WebRtcVideoMediaChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) { + return false; +} + +bool WebRtcVideoMediaChannel::RemoveStream(uint32 ssrc) { + return false; +} + +bool WebRtcVideoMediaChannel::SetRenderer( + uint32 ssrc, VideoRenderer* renderer) { + ASSERT(vie_channel_ != -1); + if (ssrc != 0) + return false; + if (remote_renderer_.get()) { + // If the renderer already set, stop it first + engine_->video_engine()->render()->StopRender(vie_channel_); + } + remote_renderer_.reset(new WebRtcRenderAdapter(renderer)); + + if (engine_->video_engine()->render()->AddRenderer(vie_channel_, + webrtc::kVideoI420, remote_renderer_.get()) != 0) { + LOG_RTCERR3(AddRenderer, vie_channel_, webrtc::kVideoI420, + remote_renderer_.get()); + remote_renderer_.reset(); + return false; + } + + if (engine_->video_engine()->render()->StartRender(vie_channel_) != 0) { + LOG_RTCERR1(StartRender, vie_channel_); + return false; + } + + return true; +} + +bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { + VideoSenderInfo sinfo; + memset(&sinfo, 0, sizeof(sinfo)); + + unsigned int ssrc; + if (engine_->video_engine()->rtp()->GetLocalSSRC(vie_channel_, + ssrc) != 0) { + LOG_RTCERR2(GetLocalSSRC, vie_channel_, ssrc); + return false; + } + sinfo.ssrc = ssrc; + + unsigned int cumulative_lost, extended_max, jitter; + int rtt_ms; + uint16 fraction_lost; + + if (engine_->video_engine()->rtp()->GetReceivedRTCPStatistics(vie_channel_, + fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms) != 0) { + LOG_RTCERR6(GetReceivedRTCPStatistics, vie_channel_, + fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms); + return false; + } + + sinfo.fraction_lost = fraction_lost; + sinfo.packets_lost = cumulative_lost; + sinfo.rtt_ms = rtt_ms; + + unsigned int bytes_sent, packets_sent, bytes_recv, packets_recv; + if (engine_->video_engine()->rtp()->GetRTPStatistics(vie_channel_, + bytes_sent, packets_sent, bytes_recv, packets_recv) != 0) { + LOG_RTCERR5(GetRTPStatistics, vie_channel_, + bytes_sent, packets_sent, bytes_recv, packets_recv); + return false; + } + sinfo.packets_sent = packets_sent; + sinfo.bytes_sent = bytes_sent; + sinfo.packets_lost = -1; + sinfo.packets_cached = -1; + + info->senders.push_back(sinfo); + + // build receiver info. + // reusing the above local variables + VideoReceiverInfo rinfo; + memset(&rinfo, 0, sizeof(rinfo)); + if (engine_->video_engine()->rtp()->GetSentRTCPStatistics(vie_channel_, + fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms) != 0) { + LOG_RTCERR6(GetSentRTCPStatistics, vie_channel_, + fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms); + return false; + } + rinfo.bytes_rcvd = bytes_recv; + rinfo.packets_rcvd = packets_recv; + rinfo.fraction_lost = fraction_lost; + rinfo.packets_lost = cumulative_lost; + + if (engine_->video_engine()->rtp()->GetRemoteSSRC(vie_channel_, + ssrc) != 0) { + return false; + } + rinfo.ssrc = ssrc; + + // Get codec for wxh + info->receivers.push_back(rinfo); + return true; +} + +bool WebRtcVideoMediaChannel::SendIntraFrame() { + bool ret = true; + if (engine()->video_engine()->codec()->SendKeyFrame(vie_channel_) != 0) { + LOG_RTCERR1(SendKeyFrame, vie_channel_); + ret = false; + } + + return ret; +} + +bool WebRtcVideoMediaChannel::RequestIntraFrame() { + // There is no API exposed to application to request a key frame + // ViE does this internally when there are errors from decoder + return false; +} + +void WebRtcVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { + engine()->video_engine()->network()->ReceivedRTPPacket(vie_channel_, + packet->data(), + packet->length()); +} + +void WebRtcVideoMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) { + engine_->video_engine()->network()->ReceivedRTCPPacket(vie_channel_, + packet->data(), + packet->length()); +} + +void WebRtcVideoMediaChannel::SetSendSsrc(uint32 id) { + if (!sending_) { + if (engine()->video_engine()->rtp()->SetLocalSSRC(vie_channel_, + id) != 0) { + LOG_RTCERR1(SetLocalSSRC, vie_channel_); + } + } else { + LOG(LS_ERROR) << "Channel already in send state"; + } +} + +bool WebRtcVideoMediaChannel::SetRtcpCName(const std::string& cname) { + if (engine()->video_engine()->rtp()->SetRTCPCName(vie_channel_, + cname.c_str()) != 0) { + LOG_RTCERR2(SetRTCPCName, vie_channel_, cname.c_str()); + return false; + } + return true; +} + +bool WebRtcVideoMediaChannel::Mute(bool on) { + // stop send?? + return false; +} + +bool WebRtcVideoMediaChannel::SetSendBandwidth(bool autobw, int bps) { + LOG(LS_INFO) << "RtcVideoMediaChanne::SetSendBandwidth"; + + if (!send_codec_.get()) { + LOG(LS_INFO) << "The send codec has not been set up yet."; + return true; + } + + if (!autobw) { + send_codec_->startBitrate = bps; + send_codec_->minBitrate = bps; + } + send_codec_->maxBitrate = bps; + + if (engine()->video_engine()->codec()->SetSendCodec(vie_channel_, + *send_codec_.get()) != 0) { + LOG_RTCERR2(SetSendCodec, vie_channel_, send_codec_->plName); + return false; + } + + return true; +} + +bool WebRtcVideoMediaChannel::SetOptions(int options) { + return true; +} + +void WebRtcVideoMediaChannel::EnableRtcp() { + engine()->video_engine()->rtp()->SetRTCPStatus( + vie_channel_, webrtc::kRtcpCompound_RFC4585); +} + +void WebRtcVideoMediaChannel::EnablePLI() { + engine_->video_engine()->rtp()->SetKeyFrameRequestMethod( + vie_channel_, webrtc::kViEKeyFrameRequestPliRtcp); +} + +void WebRtcVideoMediaChannel::EnableTMMBR() { + engine_->video_engine()->rtp()->SetTMMBRStatus(vie_channel_, true); +} + +int WebRtcVideoMediaChannel::SendPacket(int channel, const void* data, + int len) { + if (!network_interface_) { + return -1; + } + talk_base::Buffer packet(data, len, kMaxRtpPacketLen); + return network_interface_->SendPacket(&packet) ? len : -1; +} + +int WebRtcVideoMediaChannel::SendRTCPPacket(int channel, + const void* data, + int len) { + if (!network_interface_) { + return -1; + } + talk_base::Buffer packet(data, len, kMaxRtpPacketLen); + return network_interface_->SendRtcp(&packet) ? len : -1; +} + +} // namespace cricket + +#endif // HAVE_WEBRTC + diff --git a/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoengine.h b/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoengine.h new file mode 100644 index 000000000..1a966c105 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoengine.h @@ -0,0 +1,197 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_SESSION_PHONE_WEBRTCVIDEOENGINE_H_ +#define TALK_SESSION_PHONE_WEBRTCVIDEOENGINE_H_ + +#include + +#include "talk/base/scoped_ptr.h" +#include "talk/session/phone/videocommon.h" +#include "talk/session/phone/codec.h" +#include "talk/session/phone/channel.h" +#include "talk/session/phone/mediaengine.h" +#include "talk/session/phone/webrtccommon.h" + +namespace webrtc { +class VideoCaptureModule; +class VideoRender; +} + +namespace cricket { +struct Device; +class VideoRenderer; +class ViEWrapper; +class VoiceMediaChannel; +class WebRtcRenderAdapter; +class WebRtcVideoMediaChannel; +class WebRtcVoiceEngine; + +class WebRtcVideoEngine : public webrtc::ViEBaseObserver, + public webrtc::TraceCallback { + public: + // Creates the WebRtcVideoEngine with internal VideoCaptureModule. + WebRtcVideoEngine(); + // Creates the WebRtcVideoEngine, and specifies the WebRtcVoiceEngine and + // external VideoCaptureModule to use. + WebRtcVideoEngine(WebRtcVoiceEngine* voice_engine, + webrtc::VideoCaptureModule* capture); + // For testing purposes. Allows the WebRtcVoiceEngine and + // ViEWrapper to be mocks. + WebRtcVideoEngine(WebRtcVoiceEngine* voice_engine, ViEWrapper* vie_wrapper); + ~WebRtcVideoEngine(); + + bool Init(); + void Terminate(); + + WebRtcVideoMediaChannel* CreateChannel( + VoiceMediaChannel* voice_channel); + bool FindCodec(const VideoCodec& codec); + bool SetDefaultEncoderConfig(const VideoEncoderConfig& config); + + void RegisterChannel(WebRtcVideoMediaChannel* channel); + void UnregisterChannel(WebRtcVideoMediaChannel* channel); + + ViEWrapper* video_engine() { return vie_wrapper_.get(); } + int GetLastVideoEngineError(); + int GetCapabilities(); + bool SetOptions(int options); + bool SetCaptureDevice(const Device* device); + bool SetCaptureModule(webrtc::VideoCaptureModule* vcm); + bool SetLocalRenderer(VideoRenderer* renderer); + CaptureResult SetCapture(bool capture); + const std::vector& codecs() const; + void SetLogging(int min_sev, const char* filter); + + int GetLastEngineError(); + + VideoEncoderConfig& default_encoder_config() { + return default_encoder_config_; + } + + void ConvertToCricketVideoCodec(const webrtc::VideoCodec& in_codec, + VideoCodec& out_codec); + + bool ConvertFromCricketVideoCodec(const VideoCodec& in_codec, + webrtc::VideoCodec& out_codec); + + sigslot::signal1 SignalCaptureResult; + + private: + struct VideoCodecPref { + const char* payload_name; + int payload_type; + int pref; + }; + + static const VideoCodecPref kVideoCodecPrefs[]; + int GetCodecPreference(const char* name); + + void ApplyLogging(); + bool InitVideoEngine(); + void PerformanceAlarm(const unsigned int cpu_load); + bool ReleaseCaptureDevice(); + virtual void Print(const webrtc::TraceLevel level, const char* trace_string, + const int length); + + typedef std::vector VideoChannels; + + talk_base::scoped_ptr vie_wrapper_; + webrtc::VideoCaptureModule* capture_; + bool external_capture_; + int capture_id_; + webrtc::VideoRender* renderer_; + WebRtcVoiceEngine* voice_engine_; + std::vector video_codecs_; + VideoChannels channels_; + int log_level_; + VideoEncoderConfig default_encoder_config_; + bool capture_started_; + talk_base::scoped_ptr local_renderer_; +}; + +class WebRtcVideoMediaChannel : public VideoMediaChannel, + public webrtc::Transport { + public: + WebRtcVideoMediaChannel( + WebRtcVideoEngine* engine, VoiceMediaChannel* voice_channel); + ~WebRtcVideoMediaChannel(); + + bool Init(); + virtual bool SetRecvCodecs(const std::vector &codecs); + virtual bool SetSendCodecs(const std::vector &codecs); + virtual bool SetRender(bool render); + virtual bool SetSend(bool send); + virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc); + virtual bool RemoveStream(uint32 ssrc); + virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer); + virtual bool GetStats(VideoMediaInfo* info); + virtual bool SendIntraFrame(); + virtual bool RequestIntraFrame(); + + virtual void OnPacketReceived(talk_base::Buffer* packet); + virtual void OnRtcpReceived(talk_base::Buffer* packet); + virtual void SetSendSsrc(uint32 id); + virtual bool SetRtcpCName(const std::string& cname); + virtual bool Mute(bool on); + virtual bool SetRecvRtpHeaderExtensions( + const std::vector& extensions) { + return false; + } + virtual bool SetSendRtpHeaderExtensions( + const std::vector& extensions) { + return false; + } + virtual bool SetSendBandwidth(bool autobw, int bps); + virtual bool SetOptions(int options); + + WebRtcVideoEngine* engine() { return engine_; } + VoiceMediaChannel* voice_channel() { return voice_channel_; } + int video_channel() { return vie_channel_; } + bool sending() { return sending_; } + + protected: + int GetLastEngineError() { return engine()->GetLastEngineError(); } + virtual int SendPacket(int channel, const void* data, int len); + virtual int SendRTCPPacket(int channel, const void* data, int len); + + private: + void EnableRtcp(); + void EnablePLI(); + void EnableTMMBR(); + + WebRtcVideoEngine* engine_; + VoiceMediaChannel* voice_channel_; + int vie_channel_; + bool sending_; + bool render_started_; + talk_base::scoped_ptr send_codec_; + talk_base::scoped_ptr remote_renderer_; +}; +} // namespace cricket + +#endif // TALK_SESSION_PHONE_WEBRTCVIDEOENGINE_H_ diff --git a/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoframe.cc b/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoframe.cc new file mode 100644 index 000000000..405f1e0c9 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoframe.cc @@ -0,0 +1,238 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/session/phone/webrtcvideoframe.h" + +#include "talk/base/logging.h" +#include "talk/session/phone/videocommon.h" +#ifdef WEBRTC_RELATIVE_PATH +#include "common_video/vplib/main/interface/vplib.h" +#else +#include "third_party/webrtc/files/include/vplib.h" +#endif + +namespace cricket { +WebRtcVideoFrame::WebRtcVideoFrame() { +} + +WebRtcVideoFrame::~WebRtcVideoFrame() { +} + +void WebRtcVideoFrame::Attach(uint8* buffer, size_t buffer_size, size_t w, + size_t h, int64 elapsed_time, int64 time_stamp) { + video_frame_.Free(); + WebRtc_UWord8* new_memory = buffer; + WebRtc_UWord32 new_length = buffer_size; + WebRtc_UWord32 new_size = buffer_size; + video_frame_.Swap(new_memory, new_length, new_size); + video_frame_.SetWidth(w); + video_frame_.SetHeight(h); + elapsed_time_ = elapsed_time; + video_frame_.SetTimeStamp(time_stamp); +} + +void WebRtcVideoFrame::Detach(uint8** buffer, size_t* buffer_size) { + WebRtc_UWord8* new_memory = NULL; + WebRtc_UWord32 new_length = 0; + WebRtc_UWord32 new_size = 0; + video_frame_.Swap(new_memory, new_length, new_size); + *buffer = new_memory; + *buffer_size = new_size; +} + +bool WebRtcVideoFrame::InitToBlack(size_t w, size_t h, + int64 elapsed_time, int64 time_stamp) { + size_t buffer_size = w * h * 3 / 2; + uint8* buffer = new uint8[buffer_size]; + Attach(buffer, buffer_size, w, h, elapsed_time, time_stamp); + memset(GetYPlane(), 16, w * h); + memset(GetUPlane(), 128, w * h / 4); + memset(GetVPlane(), 128, w * h / 4); + return true; +} + +size_t WebRtcVideoFrame::GetWidth() const { + return video_frame_.Width(); +} + +size_t WebRtcVideoFrame::GetHeight() const { + return video_frame_.Height(); +} + +const uint8* WebRtcVideoFrame::GetYPlane() const { + WebRtc_UWord8* buffer = video_frame_.Buffer(); + return buffer; +} + +const uint8* WebRtcVideoFrame::GetUPlane() const { + WebRtc_UWord8* buffer = video_frame_.Buffer(); + if (buffer) + buffer += (video_frame_.Width() * video_frame_.Height()); + return buffer; +} + +const uint8* WebRtcVideoFrame::GetVPlane() const { + WebRtc_UWord8* buffer = video_frame_.Buffer(); + if (buffer) + buffer += (video_frame_.Width() * video_frame_.Height() * 5 / 4); + return buffer; +} + +uint8* WebRtcVideoFrame::GetYPlane() { + WebRtc_UWord8* buffer = video_frame_.Buffer(); + return buffer; +} + +uint8* WebRtcVideoFrame::GetUPlane() { + WebRtc_UWord8* buffer = video_frame_.Buffer(); + if (buffer) + buffer += (video_frame_.Width() * video_frame_.Height()); + return buffer; +} + +uint8* WebRtcVideoFrame::GetVPlane() { + WebRtc_UWord8* buffer = video_frame_.Buffer(); + if (buffer) + buffer += (video_frame_.Width() * video_frame_.Height() * 5 / 4); + return buffer; +} + +VideoFrame* WebRtcVideoFrame::Copy() const { + WebRtc_UWord8* buffer = video_frame_.Buffer(); + if (!buffer) + return NULL; + + size_t new_buffer_size = video_frame_.Length(); + uint8* new_buffer = new uint8[new_buffer_size]; + memcpy(new_buffer, buffer, new_buffer_size); + WebRtcVideoFrame* copy = new WebRtcVideoFrame(); + copy->Attach(new_buffer, new_buffer_size, + video_frame_.Width(), video_frame_.Height(), + elapsed_time_, video_frame_.TimeStamp()); + return copy; +} + +size_t WebRtcVideoFrame::CopyToBuffer( + uint8* buffer, size_t size) const { + if (!video_frame_.Buffer()) { + return 0; + } + + size_t needed = video_frame_.Length(); + if (needed <= size) { + memcpy(buffer, video_frame_.Buffer(), needed); + } + return needed; +} + +size_t WebRtcVideoFrame::ConvertToRgbBuffer(uint32 to_fourcc, + uint8* buffer, + size_t size, + size_t pitch_rgb) const { + if (!video_frame_.Buffer()) { + return 0; + } + + size_t width = video_frame_.Width(); + size_t height = video_frame_.Height(); + // See http://www.virtualdub.org/blog/pivot/entry.php?id=190 for a good + // explanation of pitch and why this is the amount of space we need. + size_t needed = pitch_rgb * (height - 1) + 4 * width; + + if (needed > size) { + LOG(LS_WARNING) << "RGB buffer is not large enough"; + return 0; + } + + webrtc::VideoType outgoingVideoType = webrtc::kUnknown; + switch (to_fourcc) { + case FOURCC_ARGB: + outgoingVideoType = webrtc::kARGB; + break; + default: + LOG(LS_WARNING) << "RGB type not supported: " << to_fourcc; + return 0; + break; + } + + if (outgoingVideoType != webrtc::kUnknown) + webrtc::ConvertFromI420(outgoingVideoType, video_frame_.Buffer(), + width, height, buffer); + + return needed; +} + +void WebRtcVideoFrame::StretchToPlanes( + uint8* y, uint8* u, uint8* v, + int32 dst_pitch_y, int32 dst_pitch_u, int32 dst_pitch_v, + size_t width, size_t height, bool interpolate, bool crop) const { + // TODO(ronghuawu): Implement StretchToPlanes +} + +size_t WebRtcVideoFrame::StretchToBuffer(size_t w, size_t h, + uint8* buffer, size_t size, + bool interpolate, + bool crop) const { + if (!video_frame_.Buffer()) { + return 0; + } + + size_t needed = video_frame_.Length(); + + if (needed <= size) { + uint8* bufy = buffer; + uint8* bufu = bufy + w * h; + uint8* bufv = bufu + ((w + 1) >> 1) * ((h + 1) >> 1); + StretchToPlanes(bufy, bufu, bufv, w, (w + 1) >> 1, (w + 1) >> 1, w, h, + interpolate, crop); + } + return needed; +} + +void WebRtcVideoFrame::StretchToFrame(VideoFrame* target, + bool interpolate, bool crop) const { + if (!target) return; + + StretchToPlanes(target->GetYPlane(), + target->GetUPlane(), + target->GetVPlane(), + target->GetYPitch(), + target->GetUPitch(), + target->GetVPitch(), + target->GetWidth(), + target->GetHeight(), + interpolate, crop); + target->SetElapsedTime(GetElapsedTime()); + target->SetTimeStamp(GetTimeStamp()); +} + +VideoFrame* WebRtcVideoFrame::Stretch(size_t w, size_t h, + bool interpolate, bool crop) const { + // TODO(ronghuawu): implement + return NULL; +} +} // namespace cricket diff --git a/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoframe.h b/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoframe.h new file mode 100644 index 000000000..96758c7f3 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/webrtcvideoframe.h @@ -0,0 +1,97 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_SESSION_PHONE_WEBRTCVIDEOFRAME_H_ +#define TALK_SESSION_PHONE_WEBRTCVIDEOFRAME_H_ + +#ifdef WEBRTC_RELATIVE_PATH +#include "common_types.h" +#include "modules/interface/module_common_types.h" +#else +#include "third_party/webrtc/files/include/common_types.h" +#include "third_party/webrtc/files/include/module_common_types.h" +#endif +#include "talk/session/phone/mediachannel.h" + +namespace cricket { +// WebRtcVideoFrame only supports I420 +class WebRtcVideoFrame : public VideoFrame { + public: + WebRtcVideoFrame(); + ~WebRtcVideoFrame(); + + void Attach(uint8* buffer, size_t buffer_size, + size_t w, size_t h, int64 elapsed_time, int64 time_stamp); + void Detach(uint8** buffer, size_t* buffer_size); + bool InitToBlack(size_t w, size_t h, int64 elapsed_time, int64 time_stamp); + bool HasImage() const { return video_frame_.Buffer() != NULL; } + + virtual size_t GetWidth() const; + virtual size_t GetHeight() const; + virtual const uint8* GetYPlane() const; + virtual const uint8* GetUPlane() const; + virtual const uint8* GetVPlane() const; + virtual uint8* GetYPlane(); + virtual uint8* GetUPlane(); + virtual uint8* GetVPlane(); + virtual int32 GetYPitch() const { return video_frame_.Width(); } + virtual int32 GetUPitch() const { return video_frame_.Width() / 2; } + virtual int32 GetVPitch() const { return video_frame_.Width() / 2; } + + virtual size_t GetPixelWidth() const { return 1; } + virtual size_t GetPixelHeight() const { return 1; } + virtual int64 GetElapsedTime() const { return elapsed_time_; } + virtual int64 GetTimeStamp() const { return video_frame_.TimeStamp(); } + virtual void SetElapsedTime(int64 elapsed_time) { + elapsed_time_ = elapsed_time; + } + virtual void SetTimeStamp(int64 time_stamp) { + video_frame_.SetTimeStamp(time_stamp); + } + + virtual VideoFrame* Copy() const; + virtual size_t CopyToBuffer(uint8* buffer, size_t size) const; + virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8* buffer, + size_t size, size_t pitch_rgb) const; + virtual void StretchToPlanes(uint8* y, uint8* u, uint8* v, + int32 pitchY, int32 pitchU, int32 pitchV, + size_t width, size_t height, + bool interpolate, bool crop) const; + virtual size_t StretchToBuffer(size_t w, size_t h, uint8* buffer, size_t size, + bool interpolate, bool crop) const; + virtual void StretchToFrame(VideoFrame* target, bool interpolate, + bool crop) const; + virtual VideoFrame* Stretch(size_t w, size_t h, bool interpolate, + bool crop) const; + + private: + webrtc::VideoFrame video_frame_; + int64 elapsed_time_; +}; +} // namespace cricket + +#endif // TALK_SESSION_PHONE_WEBRTCVIDEOFRAME_H_ diff --git a/third_party_mods/libjingle/source/talk/app/videoengine.h b/third_party_mods/libjingle/source/talk/session/phone/webrtcvie.h similarity index 50% rename from third_party_mods/libjingle/source/talk/app/videoengine.h rename to third_party_mods/libjingle/source/talk/session/phone/webrtcvie.h index 3b724bbfc..043984d98 100644 --- a/third_party_mods/libjingle/source/talk/app/videoengine.h +++ b/third_party_mods/libjingle/source/talk/session/phone/webrtcvie.h @@ -26,12 +26,16 @@ */ -#ifndef TALK_APP_WEBRTC_VIDEOENGINE_H_ -#define TALK_APP_WEBRTC_VIDEOENGINE_H_ +#ifndef TALK_SESSION_PHONE_WEBRTCVIE_H_ +#define TALK_SESSION_PHONE_WEBRTCVIE_H_ #include "talk/base/common.h" +#include "talk/session/phone/webrtccommon.h" +#ifdef WEBRTC_RELATIVE_PATH #include "common_types.h" #include "modules/interface/module_common_types.h" +#include "modules/video_capture/main/interface/video_capture.h" +#include "modules/video_render/main/interface/video_render.h" #include "video_engine/main/interface/vie_base.h" #include "video_engine/main/interface/vie_capture.h" #include "video_engine/main/interface/vie_codec.h" @@ -40,34 +44,48 @@ #include "video_engine/main/interface/vie_network.h" #include "video_engine/main/interface/vie_render.h" #include "video_engine/main/interface/vie_rtp_rtcp.h" +#else +#include "third_party/webrtc/files/include/common_types.h" +#include "third_party/webrtc/files/include/module_common_types.h" +#include "third_party/webrtc/files/include/video_capture.h" +#include "third_party/webrtc/files/include/video_render.h" +#include "third_party/webrtc/files/include/vie_base.h" +#include "third_party/webrtc/files/include/vie_capture.h" +#include "third_party/webrtc/files/include/vie_codec.h" +#include "third_party/webrtc/files/include/vie_errors.h" +#include "third_party/webrtc/files/include/vie_image_process.h" +#include "third_party/webrtc/files/include/vie_network.h" +#include "third_party/webrtc/files/include/vie_render.h" +#include "third_party/webrtc/files/include/vie_rtp_rtcp.h" +#endif // WEBRTC_RELATIVE_PATH -namespace webrtc { +namespace cricket { // all tracing macros should go to a common file // automatically handles lifetime of VideoEngine -class scoped_video_engine { +class scoped_vie_engine { public: - explicit scoped_video_engine(VideoEngine* e) : ptr(e) {} + explicit scoped_vie_engine(webrtc::VideoEngine* e) : ptr(e) {} // VERIFY, to ensure that there are no leaks at shutdown - ~scoped_video_engine() { + ~scoped_vie_engine() { if (ptr) { - VideoEngine::Delete(ptr); + webrtc::VideoEngine::Delete(ptr); } } - VideoEngine* get() const { return ptr; } + webrtc::VideoEngine* get() const { return ptr; } private: - VideoEngine* ptr; + webrtc::VideoEngine* ptr; }; // scoped_ptr class to handle obtaining and releasing VideoEngine // interface pointers -template class scoped_video_ptr { +template class scoped_vie_ptr { public: - explicit scoped_video_ptr(const scoped_video_engine& e) + explicit scoped_vie_ptr(const scoped_vie_engine& e) : ptr(T::GetInterface(e.get())) {} - explicit scoped_video_ptr(T* p) : ptr(p) {} - ~scoped_video_ptr() { if (ptr) ptr->Release(); } + explicit scoped_vie_ptr(T* p) : ptr(p) {} + ~scoped_vie_ptr() { if (ptr) ptr->Release(); } T* operator->() const { return ptr; } T* get() const { return ptr; } private: @@ -76,46 +94,50 @@ template class scoped_video_ptr { // Utility class for aggregating the various WebRTC interface. // Fake implementations can also be injected for testing. -class VideoEngineWrapper { +class ViEWrapper { public: - VideoEngineWrapper() - : engine_(VideoEngine::Create()), + ViEWrapper() + : engine_(webrtc::VideoEngine::Create()), base_(engine_), codec_(engine_), capture_(engine_), network_(engine_), render_(engine_), rtp_(engine_), image_(engine_) { } - VideoEngineWrapper(ViEBase* base, ViECodec* codec, ViECapture* capture, - ViENetwork* network, ViERender* render, - ViERTP_RTCP* rtp, ViEImageProcess* image) + ViEWrapper(webrtc::ViEBase* base, webrtc::ViECodec* codec, + webrtc::ViECapture* capture, webrtc::ViENetwork* network, + webrtc::ViERender* render, webrtc::ViERTP_RTCP* rtp, + webrtc::ViEImageProcess* image) : engine_(NULL), - base_(base), codec_(codec), capture_(capture), - network_(network), render_(render), rtp_(rtp), + base_(base), + codec_(codec), + capture_(capture), + network_(network), + render_(render), + rtp_(rtp), image_(image) { } - virtual ~VideoEngineWrapper() {} - VideoEngine* engine() { return engine_.get(); } - ViEBase* base() { return base_.get(); } - ViECodec* codec() { return codec_.get(); } - ViECapture* capture() { return capture_.get(); } - ViENetwork* network() { return network_.get(); } - ViERender* render() { return render_.get(); } - ViERTP_RTCP* rtp() { return rtp_.get(); } - ViEImageProcess* sync() { return image_.get(); } + virtual ~ViEWrapper() {} + webrtc::VideoEngine* engine() { return engine_.get(); } + webrtc::ViEBase* base() { return base_.get(); } + webrtc::ViECodec* codec() { return codec_.get(); } + webrtc::ViECapture* capture() { return capture_.get(); } + webrtc::ViENetwork* network() { return network_.get(); } + webrtc::ViERender* render() { return render_.get(); } + webrtc::ViERTP_RTCP* rtp() { return rtp_.get(); } + webrtc::ViEImageProcess* sync() { return image_.get(); } int error() { return base_->LastError(); } private: - scoped_video_engine engine_; - scoped_video_ptr base_; - scoped_video_ptr codec_; - scoped_video_ptr capture_; - scoped_video_ptr network_; - scoped_video_ptr render_; - scoped_video_ptr rtp_; - scoped_video_ptr image_; + scoped_vie_engine engine_; + scoped_vie_ptr base_; + scoped_vie_ptr codec_; + scoped_vie_ptr capture_; + scoped_vie_ptr network_; + scoped_vie_ptr render_; + scoped_vie_ptr rtp_; + scoped_vie_ptr image_; }; +} -} //namespace webrtc - -#endif // TALK_APP_WEBRTC_VOICEENGINE_H_ +#endif // TALK_SESSION_PHONE_WEBRTCVIE_H_ diff --git a/third_party_mods/libjingle/source/talk/session/phone/webrtcvoe.h b/third_party_mods/libjingle/source/talk/session/phone/webrtcvoe.h new file mode 100644 index 000000000..ee5011b08 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/webrtcvoe.h @@ -0,0 +1,190 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef TALK_SESSION_PHONE_WEBRTCVOE_H_ +#define TALK_SESSION_PHONE_WEBRTCVOE_H_ + +#include "talk/base/common.h" +#include "talk/session/phone/webrtccommon.h" + +#ifdef WEBRTC_RELATIVE_PATH +#include "common_types.h" +#include "modules/audio_device/main/interface/audio_device.h" +#include "voice_engine/main/interface/voe_audio_processing.h" +#include "voice_engine/main/interface/voe_base.h" +#include "voice_engine/main/interface/voe_codec.h" +#include "voice_engine/main/interface/voe_dtmf.h" +#include "voice_engine/main/interface/voe_errors.h" +#include "voice_engine/main/interface/voe_file.h" +#include "voice_engine/main/interface/voe_hardware.h" +#include "voice_engine/main/interface/voe_neteq_stats.h" +#include "voice_engine/main/interface/voe_network.h" +#include "voice_engine/main/interface/voe_rtp_rtcp.h" +#include "voice_engine/main/interface/voe_video_sync.h" +#include "voice_engine/main/interface/voe_volume_control.h" +#else +#include "third_party/webrtc/files/include/audio_device.h" +#include "third_party/webrtc/files/include/common_types.h" +#include "third_party/webrtc/files/include/voe_audio_processing.h" +#include "third_party/webrtc/files/include/voe_base.h" +#include "third_party/webrtc/files/include/voe_codec.h" +#include "third_party/webrtc/files/include/voe_dtmf.h" +#include "third_party/webrtc/files/include/voe_errors.h" +#include "third_party/webrtc/files/include/voe_file.h" +#include "third_party/webrtc/files/include/voe_hardware.h" +#include "third_party/webrtc/files/include/voe_neteq_stats.h" +#include "third_party/webrtc/files/include/voe_network.h" +#include "third_party/webrtc/files/include/voe_rtp_rtcp.h" +#include "third_party/webrtc/files/include/voe_video_sync.h" +#include "third_party/webrtc/files/include/voe_volume_control.h" +#endif // WEBRTC_RELATIVE_PATH + +namespace cricket { +// automatically handles lifetime of WebRtc VoiceEngine +class scoped_voe_engine { + public: + explicit scoped_voe_engine(webrtc::VoiceEngine* e) : ptr(e) {} + // VERIFY, to ensure that there are no leaks at shutdown + ~scoped_voe_engine() { if (ptr) VERIFY(webrtc::VoiceEngine::Delete(ptr)); } + // Releases the current pointer. + void reset() { + if (ptr) { + VERIFY(webrtc::VoiceEngine::Delete(ptr)); + ptr = NULL; + } + } + webrtc::VoiceEngine* get() const { return ptr; } + private: + webrtc::VoiceEngine* ptr; +}; + +// scoped_ptr class to handle obtaining and releasing WebRTC interface pointers +template +class scoped_voe_ptr { + public: + explicit scoped_voe_ptr(const scoped_voe_engine& e) + : ptr(T::GetInterface(e.get())) {} + explicit scoped_voe_ptr(T* p) : ptr(p) {} + ~scoped_voe_ptr() { if (ptr) ptr->Release(); } + T* operator->() const { return ptr; } + T* get() const { return ptr; } + + // Releases the current pointer. + void reset() { + if (ptr) { + ptr->Release(); + ptr = NULL; + } + } + + private: + T* ptr; +}; + +// Utility class for aggregating the various WebRTC interface. +// Fake implementations can also be injected for testing. +class VoEWrapper { + public: + VoEWrapper() + : engine_(webrtc::VoiceEngine::Create()), processing_(engine_), + base_(engine_), codec_(engine_), dtmf_(engine_), file_(engine_), + hw_(engine_), neteq_(engine_), network_(engine_), rtp_(engine_), + sync_(engine_), volume_(engine_) { + } + VoEWrapper(webrtc::VoEAudioProcessing* processing, + webrtc::VoEBase* base, + webrtc::VoECodec* codec, + webrtc::VoEDtmf* dtmf, + webrtc::VoEFile* file, + webrtc::VoEHardware* hw, + webrtc::VoENetEqStats* neteq, + webrtc::VoENetwork* network, + webrtc::VoERTP_RTCP* rtp, + webrtc::VoEVideoSync* sync, + webrtc::VoEVolumeControl* volume) + : engine_(NULL), + processing_(processing), + base_(base), + codec_(codec), + dtmf_(dtmf), + file_(file), + hw_(hw), + neteq_(neteq), + network_(network), + rtp_(rtp), + sync_(sync), + volume_(volume) { + } + ~VoEWrapper() {} + webrtc::VoiceEngine* engine() const { return engine_.get(); } + webrtc::VoEAudioProcessing* processing() const { return processing_.get(); } + webrtc::VoEBase* base() const { return base_.get(); } + webrtc::VoECodec* codec() const { return codec_.get(); } + webrtc::VoEDtmf* dtmf() const { return dtmf_.get(); } + webrtc::VoEFile* file() const { return file_.get(); } + webrtc::VoEHardware* hw() const { return hw_.get(); } + webrtc::VoENetEqStats* neteq() const { return neteq_.get(); } + webrtc::VoENetwork* network() const { return network_.get(); } + webrtc::VoERTP_RTCP* rtp() const { return rtp_.get(); } + webrtc::VoEVideoSync* sync() const { return sync_.get(); } + webrtc::VoEVolumeControl* volume() const { return volume_.get(); } + int error() { return base_->LastError(); } + + private: + scoped_voe_engine engine_; + scoped_voe_ptr processing_; + scoped_voe_ptr base_; + scoped_voe_ptr codec_; + scoped_voe_ptr dtmf_; + scoped_voe_ptr file_; + scoped_voe_ptr hw_; + scoped_voe_ptr neteq_; + scoped_voe_ptr network_; + scoped_voe_ptr rtp_; + scoped_voe_ptr sync_; + scoped_voe_ptr volume_; +}; + +// Adds indirection to static WebRtc functions, allowing them to be mocked. +class VoETraceWrapper { + public: + virtual ~VoETraceWrapper() {} + + virtual int SetTraceFilter(const unsigned int filter) { + return webrtc::VoiceEngine::SetTraceFilter(filter); + } + virtual int SetTraceFile(const char* fileNameUTF8) { + return webrtc::VoiceEngine::SetTraceFile(fileNameUTF8); + } + virtual int SetTraceCallback(webrtc::TraceCallback* callback) { + return webrtc::VoiceEngine::SetTraceCallback(callback); + } +}; +} + +#endif // TALK_SESSION_PHONE_WEBRTCVOE_H_ diff --git a/third_party_mods/libjingle/source/talk/session/phone/webrtcvoiceengine.cc b/third_party_mods/libjingle/source/talk/session/phone/webrtcvoiceengine.cc new file mode 100644 index 000000000..aa0bacb35 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/webrtcvoiceengine.cc @@ -0,0 +1,1896 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// shhhhh{ +#ifdef HAVE_CONFIG_H +#include +#endif +// shhhhh} + +#ifdef HAVE_WEBRTC + +#include "talk/session/phone/webrtcvoiceengine.h" + +#include +#include +#include +#include + +#include "talk/base/base64.h" +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/helpers.h" +#include "talk/base/logging.h" +#include "talk/base/stringencode.h" +#include "talk/session/phone/webrtcvoe.h" + +#ifdef WIN32 +#include // NOLINT +#endif + +namespace cricket { + +// For Linux/Mac, using the default device is done by specifying index 0 for +// VoE 4.0 and not -1 (which was the case for VoE 3.5). +// +// On Windows Vista and newer, Microsoft introduced the concept of "Default +// Communications Device". This means that there are two types of default +// devices (old Wave Audio style default and Default Communications Device). +// +// On Windows systems which only support Wave Audio style default, uses either +// -1 or 0 to select the default device. +// +// On Windows systems which support both "Default Communication Device" and +// old Wave Audio style default, use -1 for Default Communications Device and +// -2 for Wave Audio style default, which is what we want to use for clips. +// It's not clear yet whether the -2 index is handled properly on other OSes. + +#ifdef WIN32 +static const int kDefaultAudioDeviceId = -1; +static const int kDefaultSoundclipDeviceId = -2; +#else +static const int kDefaultAudioDeviceId = 0; +#endif + +// extension header for audio levels, as defined in +// http://tools.ietf.org/html/draft-ietf-avtext-client-to-mixer-audio-level-01 +static const char kRtpAudioLevelHeaderExtension[] = + "urn:ietf:params:rtp-hdrext:audio-level"; + +static void LogMultiline(talk_base::LoggingSeverity sev, char* text) { + const char* delim = "\r\n"; + for (char* tok = strtok(text, delim); tok; tok = strtok(NULL, delim)) { + LOG_V(sev) << tok; + } +} + +// WebRtcVoiceEngine +const WebRtcVoiceEngine::CodecPref WebRtcVoiceEngine::kCodecPrefs[] = { + { "ISAC", 16000 }, + { "ISAC", 32000 }, + { "ISACLC", 16000 }, + { "speex", 16000 }, + { "IPCMWB", 16000 }, + { "G722", 16000 }, + { "iLBC", 8000 }, + { "speex", 8000 }, + { "GSM", 8000 }, + { "EG711U", 8000 }, + { "EG711A", 8000 }, + { "PCMU", 8000 }, + { "PCMA", 8000 }, + { "CN", 32000 }, + { "CN", 16000 }, + { "CN", 8000 }, + { "red", 8000 }, + { "telephone-event", 8000 }, +}; + +class WebRtcSoundclipMedia : public SoundclipMedia { + public: + explicit WebRtcSoundclipMedia(WebRtcVoiceEngine *engine) + : engine_(engine), webrtc_channel_(-1) { + engine_->RegisterSoundclip(this); + } + + virtual ~WebRtcSoundclipMedia() { + engine_->UnregisterSoundclip(this); + if (webrtc_channel_ != -1) { + if (engine_->voe_sc()->base()->DeleteChannel(webrtc_channel_) + == -1) { + LOG_RTCERR1(DeleteChannel, webrtc_channel_); + } + } + } + + bool Init() { + webrtc_channel_ = engine_->voe_sc()->base()->CreateChannel(); + if (webrtc_channel_ == -1) { + LOG_RTCERR0(CreateChannel); + return false; + } + return true; + } + + bool Enable() { + if (engine_->voe_sc()->base()->StartPlayout(webrtc_channel_) == -1) { + LOG_RTCERR1(StartPlayout, webrtc_channel_); + return false; + } + return true; + } + + bool Disable() { + if (engine_->voe_sc()->base()->StopPlayout(webrtc_channel_) == -1) { + LOG_RTCERR1(StopPlayout, webrtc_channel_); + return false; + } + return true; + } + + virtual bool PlaySound(const char *buf, int len, int flags) { + // Must stop playing the current sound (if any), because we are about to + // modify the stream. + if (engine_->voe_sc()->file()->StopPlayingFileLocally(webrtc_channel_) + == -1) { + LOG_RTCERR1(StopPlayingFileLocally, webrtc_channel_); + return false; + } + + if (buf) { + stream_.reset(new WebRtcSoundclipStream(buf, len)); + stream_->set_loop((flags & SF_LOOP) != 0); + stream_->Rewind(); + + // Play it. + if (engine_->voe_sc()->file()->StartPlayingFileLocally( + webrtc_channel_, stream_.get()) == -1) { + LOG_RTCERR2(StartPlayingFileLocally, webrtc_channel_, stream_.get()); + LOG(LS_ERROR) << "Unable to start soundclip"; + return false; + } + } else { + stream_.reset(); + } + return true; + } + + int GetLastEngineError() const { return engine_->voe_sc()->error(); } + + private: + WebRtcVoiceEngine *engine_; + int webrtc_channel_; + talk_base::scoped_ptr stream_; +}; + +WebRtcVoiceEngine::WebRtcVoiceEngine() + : voe_wrapper_(new VoEWrapper()), + voe_wrapper_sc_(new VoEWrapper()), + tracing_(new VoETraceWrapper()), + adm_(NULL), + adm_sc_(NULL), + log_level_(kDefaultLogSeverity), + is_dumping_aec_(false), + desired_local_monitor_enable_(false) { + Construct(); +} + +WebRtcVoiceEngine::WebRtcVoiceEngine(webrtc::AudioDeviceModule* adm, + webrtc::AudioDeviceModule* adm_sc) + : voe_wrapper_(new VoEWrapper()), + voe_wrapper_sc_(new VoEWrapper()), + tracing_(new VoETraceWrapper()), + adm_(adm), + adm_sc_(adm_sc), + log_level_(kDefaultLogSeverity), + is_dumping_aec_(false), + desired_local_monitor_enable_(false) { + Construct(); +} + +WebRtcVoiceEngine::WebRtcVoiceEngine(VoEWrapper* voe_wrapper, + VoEWrapper* voe_wrapper_sc, + VoETraceWrapper* tracing) + : voe_wrapper_(voe_wrapper), + voe_wrapper_sc_(voe_wrapper_sc), + tracing_(tracing), + adm_(NULL), + adm_sc_(NULL), + log_level_(kDefaultLogSeverity), + is_dumping_aec_(false), + desired_local_monitor_enable_(false) { + Construct(); +} + +void WebRtcVoiceEngine::Construct() { + LOG(LS_VERBOSE) << "WebRtcVoiceEngine::WebRtcVoiceEngine"; + ApplyLogging(); + if (tracing_->SetTraceCallback(this) == -1) { + LOG_RTCERR0(SetTraceCallback); + } + if (voe_wrapper_->base()->RegisterVoiceEngineObserver(*this) == -1) { + LOG_RTCERR0(RegisterVoiceEngineObserver); + } + // Clear the default agc state. + memset(&default_agc_config_, 0, sizeof(default_agc_config_)); + + // Load our audio codec list + LOG(LS_INFO) << "WebRtc VoiceEngine codecs:"; + int ncodecs = voe_wrapper_->codec()->NumOfCodecs(); + for (int i = 0; i < ncodecs; ++i) { + webrtc::CodecInst gcodec; + if (voe_wrapper_->codec()->GetCodec(i, gcodec) >= 0) { + int pref = GetCodecPreference(gcodec.plname, gcodec.plfreq); + if (pref != -1) { + if (gcodec.rate == -1) gcodec.rate = 0; + AudioCodec codec(gcodec.pltype, gcodec.plname, gcodec.plfreq, + gcodec.rate, gcodec.channels, pref); + LOG(LS_INFO) << gcodec.plname << "/" << gcodec.plfreq << "/" \ + << gcodec.channels << " " << gcodec.pltype; + codecs_.push_back(codec); + } + } + } + // Make sure they are in local preference order + std::sort(codecs_.begin(), codecs_.end(), &AudioCodec::Preferable); +} + +WebRtcVoiceEngine::~WebRtcVoiceEngine() { + LOG(LS_VERBOSE) << "WebRtcVoiceEngine::~WebRtcVoiceEngine"; + if (voe_wrapper_->base()->DeRegisterVoiceEngineObserver() == -1) { + LOG_RTCERR0(DeRegisterVoiceEngineObserver); + } + if (adm_) { + voe_wrapper_.reset(); + webrtc::AudioDeviceModule::Destroy(adm_); + adm_ = NULL; + } + if (adm_sc_) { + voe_wrapper_sc_.reset(); + webrtc::AudioDeviceModule::Destroy(adm_sc_); + adm_sc_ = NULL; + } + + tracing_->SetTraceCallback(NULL); +} + +bool WebRtcVoiceEngine::Init() { + LOG(LS_INFO) << "WebRtcVoiceEngine::Init"; + bool res = InitInternal(); + if (res) { + LOG(LS_INFO) << "WebRtcVoiceEngine::Init Done!"; + } else { + LOG(LS_ERROR) << "WebRtcVoiceEngine::Init failed"; + Terminate(); + } + return res; +} + +bool WebRtcVoiceEngine::InitInternal() { + // Temporarily turn logging level up for the Init call + int old_level = log_level_; + log_level_ = talk_base::_min(log_level_, + static_cast(talk_base::LS_INFO)); + ApplyLogging(); + + if (adm_) { + if (voe_wrapper_->base()->RegisterAudioDeviceModule(*adm_) == -1) { + LOG_RTCERR0_EX(Init, voe_wrapper_->error()); + return false; + } + } + if (adm_sc_) { + if (voe_wrapper_sc_->base()->RegisterAudioDeviceModule(*adm_sc_) == -1) { + LOG_RTCERR0_EX(Init, voe_wrapper_sc_->error()); + return false; + } + } + + // Init WebRtc VoiceEngine, enabling AEC logging if specified in SetLogging. + if (voe_wrapper_->base()->Init() == -1) { + LOG_RTCERR0_EX(Init, voe_wrapper_->error()); + return false; + } + + // Restore the previous log level + log_level_ = old_level; + ApplyLogging(); + + // Log the VoiceEngine version info + char buffer[1024] = ""; + voe_wrapper_->base()->GetVersion(buffer); + LOG(LS_INFO) << "WebRtc VoiceEngine Version:"; + LogMultiline(talk_base::LS_INFO, buffer); + + // Turn on AEC and AGC by default. + if (!SetOptions( + MediaEngine::ECHO_CANCELLATION | MediaEngine::AUTO_GAIN_CONTROL)) { + return false; + } + + // Save the default AGC configuration settings. + if (voe_wrapper_->processing()->SetAgcConfig(default_agc_config_) == -1) { + LOG_RTCERR0(GetAGCConfig); + return false; + } + +#if !defined(IOS) && !defined(ANDROID) + // VoiceEngine team recommends turning on noise reduction + // with low agressiveness. + if (voe_wrapper_->processing()->SetNsStatus(true) == -1) { +#else + // On mobile, VoiceEngine team recommends moderate aggressiveness. + if (voe_wrapper_->processing()->SetNsStatus(true, + kNsModerateSuppression) == -1) { +#endif + LOG_RTCERR1(SetNsStatus, true); + return false; + } + +#if !defined(IOS) && !defined(ANDROID) + // Enable detection for keyboard typing. + if (voe_wrapper_->processing()->SetTypingDetectionStatus(true) == -1) { + // In case of error, log the info and continue. + LOG_RTCERR1(SetTypingDetectionStatus, true); + } +#endif + + // Print our codec list again for the call diagnostic log + LOG(LS_INFO) << "WebRtc VoiceEngine codecs:"; + for (std::vector::const_iterator it = codecs_.begin(); + it != codecs_.end(); ++it) { + LOG(LS_INFO) << it->name << "/" << it->clockrate << "/" + << it->channels << " " << it->id; + } + +#if defined(LINUX) && !defined(HAVE_LIBPULSE) + voe_wrapper_sc_->hw()->SetAudioDeviceLayer(webrtc::kAudioLinuxAlsa); +#endif + + // Initialize the VoiceEngine instance that we'll use to play out sound clips. + if (voe_wrapper_sc_->base()->Init() == -1) { + LOG_RTCERR0_EX(Init, voe_wrapper_sc_->error()); + return false; + } + + // On Windows, tell it to use the default sound (not communication) devices. + // First check whether there is a valid sound device for playback. + // TODO(juberti): Clean this up when we support setting the soundclip device. +#ifdef WIN32 + int num_of_devices = 0; + if (voe_wrapper_sc_->hw()->GetNumOfPlayoutDevices(num_of_devices) != -1 && + num_of_devices > 0) { + if (voe_wrapper_sc_->hw()->SetPlayoutDevice(kDefaultSoundclipDeviceId) + == -1) { + LOG_RTCERR1_EX(SetPlayoutDevice, kDefaultSoundclipDeviceId, + voe_wrapper_sc_->error()); + return false; + } + } else { + LOG(LS_WARNING) << "No valid sound playout device found."; + } +#endif + + return true; +} + +void WebRtcVoiceEngine::Terminate() { + LOG(LS_INFO) << "WebRtcVoiceEngine::Terminate"; + + if (is_dumping_aec_) { + if (voe_wrapper_->processing()->StopDebugRecording() == -1) { + LOG_RTCERR0(StopDebugRecording); + } + is_dumping_aec_ = false; + } + + voe_wrapper_sc_->base()->Terminate(); + voe_wrapper_->base()->Terminate(); + desired_local_monitor_enable_ = false; +} + +int WebRtcVoiceEngine::GetCapabilities() { + return MediaEngine::AUDIO_SEND | MediaEngine::AUDIO_RECV; +} + +VoiceMediaChannel *WebRtcVoiceEngine::CreateChannel() { + WebRtcVoiceMediaChannel* ch = new WebRtcVoiceMediaChannel(this); + if (!ch->valid()) { + delete ch; + ch = NULL; + } + return ch; +} + +SoundclipMedia *WebRtcVoiceEngine::CreateSoundclip() { + WebRtcSoundclipMedia *soundclip = new WebRtcSoundclipMedia(this); + if (!soundclip->Init() || !soundclip->Enable()) { + delete soundclip; + return NULL; + } + return soundclip; +} + +bool WebRtcVoiceEngine::SetOptions(int options) { + // WebRtc team tells us that "auto" mode doesn't work too well, + // so we don't use it. + bool aec = (options & MediaEngine::ECHO_CANCELLATION) ? true : false; + bool agc = (options & MediaEngine::AUTO_GAIN_CONTROL) ? true : false; + +#if defined(IOS) || defined(ANDROID) + if (voe_wrapper_->processing()->SetEcStatus(aec, kEcAecm) == -1) { +#else + if (voe_wrapper_->processing()->SetEcStatus(aec) == -1) { +#endif + LOG_RTCERR1(SetEcStatus, aec); + return false; + } + + if (voe_wrapper_->processing()->SetAgcStatus(agc) == -1) { + LOG_RTCERR1(SetAgcStatus, agc); + return false; + } + + return true; +} + +struct ResumeEntry { + ResumeEntry(WebRtcVoiceMediaChannel *c, bool p, SendFlags s) + : channel(c), + playout(p), + send(s) { + } + + WebRtcVoiceMediaChannel *channel; + bool playout; + SendFlags send; +}; + +// TODO(juberti): Refactor this so that the core logic can be used to set the +// soundclip device. At that time, reinstate the soundclip pause/resume code. +bool WebRtcVoiceEngine::SetDevices(const Device* in_device, + const Device* out_device) { +#if !defined(IOS) && !defined(ANDROID) + int in_id = in_device ? talk_base::FromString(in_device->id) : + kDefaultAudioDeviceId; + int out_id = out_device ? talk_base::FromString(out_device->id) : + kDefaultAudioDeviceId; + // The device manager uses -1 as the default device, which was the case for + // VoE 3.5. VoE 4.0, however, uses 0 as the default in Linux and Mac. +#ifndef WIN32 + if (-1 == in_id) { + in_id = kDefaultAudioDeviceId; + } + if (-1 == out_id) { + out_id = kDefaultAudioDeviceId; + } +#endif + + std::string in_name = (in_id != kDefaultAudioDeviceId) ? + in_device->name : "Default device"; + std::string out_name = (out_id != kDefaultAudioDeviceId) ? + out_device->name : "Default device"; + LOG(LS_INFO) << "Setting microphone to (id=" << in_id << ", name=" << in_name + << ") and speaker to (id=" << out_id << ", name=" << out_name + << ")"; + + // If we're running the local monitor, we need to stop it first. + bool ret = true; + if (!PauseLocalMonitor()) { + LOG(LS_WARNING) << "Failed to pause local monitor"; + ret = false; + } + + // Must also pause all audio playback and capture. + for (ChannelList::const_iterator i = channels_.begin(); + i != channels_.end(); ++i) { + WebRtcVoiceMediaChannel *channel = *i; + if (!channel->PausePlayout()) { + LOG(LS_WARNING) << "Failed to pause playout"; + ret = false; + } + if (!channel->PauseSend()) { + LOG(LS_WARNING) << "Failed to pause send"; + ret = false; + } + } + + // Find the recording device id in VoiceEngine and set recording device. + if (!FindWebRtcAudioDeviceId(true, in_name, in_id, &in_id)) { + ret = false; + } + if (ret) { + if (voe_wrapper_->hw()->SetRecordingDevice(in_id) == -1) { + LOG_RTCERR2(SetRecordingDevice, in_device->name, in_id); + ret = false; + } + } + + // Find the playout device id in VoiceEngine and set playout device. + if (!FindWebRtcAudioDeviceId(false, out_name, out_id, &out_id)) { + LOG(LS_WARNING) << "Failed to find VoiceEngine device id for " << out_name; + ret = false; + } + if (ret) { + if (voe_wrapper_->hw()->SetPlayoutDevice(out_id) == -1) { + LOG_RTCERR2(SetPlayoutDevice, out_device->name, out_id); + ret = false; + } + } + + // Resume all audio playback and capture. + for (ChannelList::const_iterator i = channels_.begin(); + i != channels_.end(); ++i) { + WebRtcVoiceMediaChannel *channel = *i; + if (!channel->ResumePlayout()) { + LOG(LS_WARNING) << "Failed to resume playout"; + ret = false; + } + if (!channel->ResumeSend()) { + LOG(LS_WARNING) << "Failed to resume send"; + ret = false; + } + } + + // Resume local monitor. + if (!ResumeLocalMonitor()) { + LOG(LS_WARNING) << "Failed to resume local monitor"; + ret = false; + } + + if (ret) { + LOG(LS_INFO) << "Set microphone to (id=" << in_id <<" name=" << in_name + << ") and speaker to (id="<< out_id << " name=" << out_name + << ")"; + } + + return ret; +#else + return true; +#endif // !IOS && !ANDROID +} + +bool WebRtcVoiceEngine::FindWebRtcAudioDeviceId( + bool is_input, const std::string& dev_name, int dev_id, int* rtc_id) { + // In Linux, VoiceEngine uses the same device dev_id as the device manager. +#ifdef LINUX + *rtc_id = dev_id; + return true; +#else + // In Windows and Mac, we need to find the VoiceEngine device id by name + // unless the input dev_id is the default device id. + if (kDefaultAudioDeviceId == dev_id) { + *rtc_id = dev_id; + return true; + } + + // Get the number of VoiceEngine audio devices. + int count = 0; + if (is_input) { + if (-1 == voe_wrapper_->hw()->GetNumOfRecordingDevices(count)) { + LOG_RTCERR0(GetNumOfRecordingDevices); + return false; + } + } else { + if (-1 == voe_wrapper_->hw()->GetNumOfPlayoutDevices(count)) { + LOG_RTCERR0(GetNumOfPlayoutDevices); + return false; + } + } + + for (int i = 0; i < count; ++i) { + char name[128]; + char guid[128]; + if (is_input) { + voe_wrapper_->hw()->GetRecordingDeviceName(i, name, guid); + LOG(LS_VERBOSE) << "VoiceEngine microphone " << i << ": " << name; + } else { + voe_wrapper_->hw()->GetPlayoutDeviceName(i, name, guid); + LOG(LS_VERBOSE) << "VoiceEngine speaker " << i << ": " << name; + } + + std::string webrtc_name(name); + if (dev_name.compare(0, webrtc_name.size(), webrtc_name) == 0) { + *rtc_id = i; + return true; + } + } + LOG(LS_WARNING) << "VoiceEngine cannot find device: " << dev_name; + return false; +#endif +} + +bool WebRtcVoiceEngine::GetOutputVolume(int* level) { + unsigned int ulevel; + if (voe_wrapper_->volume()->GetSpeakerVolume(ulevel) == -1) { + LOG_RTCERR1(GetSpeakerVolume, level); + return false; + } + *level = ulevel; + return true; +} + +bool WebRtcVoiceEngine::SetOutputVolume(int level) { + ASSERT(level >= 0 && level <= 255); + if (voe_wrapper_->volume()->SetSpeakerVolume(level) == -1) { + LOG_RTCERR1(SetSpeakerVolume, level); + return false; + } + return true; +} + +int WebRtcVoiceEngine::GetInputLevel() { + unsigned int ulevel; + return (voe_wrapper_->volume()->GetSpeechInputLevel(ulevel) != -1) ? + static_cast(ulevel) : -1; +} + +bool WebRtcVoiceEngine::SetLocalMonitor(bool enable) { + desired_local_monitor_enable_ = enable; + return ChangeLocalMonitor(desired_local_monitor_enable_); +} + +bool WebRtcVoiceEngine::ChangeLocalMonitor(bool enable) { + if (enable && !monitor_.get()) { + monitor_.reset(new WebRtcMonitorStream); + if (voe_wrapper_->file()->StartRecordingMicrophone(monitor_.get()) == -1) { + LOG_RTCERR1(StartRecordingMicrophone, monitor_.get()); + // Must call Stop() because there are some cases where Start will report + // failure but still change the state, and if we leave VE in the on state + // then it could crash later when trying to invoke methods on our monitor. + voe_wrapper_->file()->StopRecordingMicrophone(); + monitor_.reset(); + return false; + } + } else if (!enable && monitor_.get()) { + voe_wrapper_->file()->StopRecordingMicrophone(); + monitor_.reset(); + } + return true; +} + +bool WebRtcVoiceEngine::PauseLocalMonitor() { + return ChangeLocalMonitor(false); +} + +bool WebRtcVoiceEngine::ResumeLocalMonitor() { + return ChangeLocalMonitor(desired_local_monitor_enable_); +} + +const std::vector& WebRtcVoiceEngine::codecs() { + return codecs_; +} + +bool WebRtcVoiceEngine::FindCodec(const AudioCodec& in) { + return FindWebRtcCodec(in, NULL); +} + +bool WebRtcVoiceEngine::FindWebRtcCodec(const AudioCodec& in, + webrtc::CodecInst* out) { + int ncodecs = voe_wrapper_->codec()->NumOfCodecs(); + for (int i = 0; i < ncodecs; ++i) { + webrtc::CodecInst gcodec; + if (voe_wrapper_->codec()->GetCodec(i, gcodec) >= 0) { + AudioCodec codec(gcodec.pltype, gcodec.plname, + gcodec.plfreq, gcodec.rate, gcodec.channels, 0); + if (codec.Matches(in)) { + if (out) { + // If the codec is VBR and an explicit rate is specified, use it. + if (in.bitrate != 0 && gcodec.rate == -1) { + gcodec.rate = in.bitrate; + } + *out = gcodec; + } + return true; + } + } + } + return false; +} + +// We suppport three different logging settings for VoiceEngine: +// 1. Observer callback that goes into talk diagnostic logfile. +// Use --logfile and --loglevel +// +// 2. Encrypted VoiceEngine log for debugging VoiceEngine. +// Use --voice_loglevel --voice_logfilter "tracefile file_name" +// +// 3. EC log and dump for debugging QualityEngine. +// Use --voice_loglevel --voice_logfilter "recordEC file_name" +// +// For more details see: "https://sites.google.com/a/google.com/wavelet/Home/ +// Magic-Flute--RTC-Engine-/Magic-Flute-Command-Line-Parameters" +void WebRtcVoiceEngine::SetLogging(int min_sev, const char* filter) { + // if min_sev == -1, we keep the current log level. + if (min_sev >= 0) { + log_level_ = min_sev; + } + + // voice log level + ApplyLogging(); + + std::vector opts; + talk_base::tokenize(filter, ' ', &opts); + + // voice log file + std::vector::iterator tracefile = + std::find(opts.begin(), opts.end(), "tracefile"); + if (tracefile != opts.end() && ++tracefile != opts.end()) { + // Write encrypted debug output (at same loglevel) to file + // EncryptedTraceFile no longer supported. + if (tracing_->SetTraceFile(tracefile->c_str()) == -1) { + LOG_RTCERR1(SetTraceFile, *tracefile); + } + } + + // AEC dump file + std::vector::iterator recordEC = + std::find(opts.begin(), opts.end(), "recordEC"); + if (recordEC != opts.end()) { + ++recordEC; + if (recordEC != opts.end() && !is_dumping_aec_) { + // Start dumping AEC when we are not dumping and recordEC has a filename. + if (voe_wrapper_->processing()->StartDebugRecording( + recordEC->c_str()) == -1) { + LOG_RTCERR0(StartDebugRecording); + } else { + is_dumping_aec_ = true; + } + } else if (recordEC == opts.end() && is_dumping_aec_) { + // Stop dumping EC when we are dumping and recordEC has no filename. + if (voe_wrapper_->processing()->StopDebugRecording() == -1) { + LOG_RTCERR0(StopDebugRecording); + } + is_dumping_aec_ = false; + } + } +} + +int WebRtcVoiceEngine::GetLastEngineError() { + return voe_wrapper_->error(); +} + +void WebRtcVoiceEngine::ApplyLogging() { + int filter = 0; + switch (log_level_) { + case talk_base::LS_VERBOSE: + filter |= webrtc::kTraceAll; // fall through + case talk_base::LS_INFO: + filter |= webrtc::kTraceStateInfo; // fall through + case talk_base::LS_WARNING: + filter |= (webrtc::kTraceInfo | webrtc::kTraceWarning); // fall through + case talk_base::LS_ERROR: + filter |= (webrtc::kTraceError | webrtc::kTraceCritical); + } + tracing_->SetTraceFilter(filter); +} + +// Ignore spammy trace messages, mostly from the stats API when we haven't +// gotten RTCP info yet from the remote side. +static bool ShouldIgnoreTrace(const std::string& trace) { + static const char* kTracesToIgnore[] = { + "\tfailed to GetReportBlockInformation", + "GetRecCodec() failed to get received codec", + "GetRemoteRTCPData() failed to retrieve sender info for remote side", + "GetRTPStatistics() failed to measure RTT since no RTP packets have been received yet", // NOLINT + "GetRTPStatistics() failed to read RTP statistics from the RTP/RTCP module", + "GetRTPStatistics() failed to retrieve RTT from the RTP/RTCP module", + "RTCPReceiver::SenderInfoReceived No received SR", + "StatisticsRTP() no statisitics availble", + NULL + }; + for (const char* const* p = kTracesToIgnore; *p; ++p) { + if (trace.find(*p) == 0) { + return true; + } + } + return false; +} + +void WebRtcVoiceEngine::Print(const webrtc::TraceLevel level, + const char* trace, const int length) { + talk_base::LoggingSeverity sev = talk_base::LS_VERBOSE; + if (level == webrtc::kTraceError || level == webrtc::kTraceCritical) + sev = talk_base::LS_ERROR; + else if (level == webrtc::kTraceWarning) + sev = talk_base::LS_WARNING; + else if (level == webrtc::kTraceStateInfo || level == webrtc::kTraceInfo) + sev = talk_base::LS_INFO; + + if (sev >= log_level_) { + // Skip past boilerplate prefix text + if (length < 72) { + std::string msg(trace, length); + LOG(LS_ERROR) << "Malformed webrtc log message: "; + LOG_V(sev) << msg; + } else { + std::string msg(trace + 71, length - 72); + if (!ShouldIgnoreTrace(msg)) { + LOG_V(sev) << "WebRtc VoE:" << msg; + } + } + } +} + +void WebRtcVoiceEngine::CallbackOnError(const int channel_num, + const int err_code) { + talk_base::CritScope lock(&channels_cs_); + WebRtcVoiceMediaChannel* channel = NULL; + uint32 ssrc = 0; + LOG(LS_WARNING) << "VoiceEngine error " << err_code << " reported on channel " + << channel_num << "."; + if (FindChannelAndSsrc(channel_num, &channel, &ssrc)) { + ASSERT(channel != NULL); + channel->OnError(ssrc, err_code); + } else { + LOG(LS_ERROR) << "VoiceEngine channel " << channel_num + << " could not be found in the channel list when error reported."; + } +} + +int WebRtcVoiceEngine::GetCodecPreference(const char *name, int clockrate) { + for (size_t i = 0; i < ARRAY_SIZE(kCodecPrefs); ++i) { + if ((strcmp(kCodecPrefs[i].name, name) == 0) && + (kCodecPrefs[i].clockrate == clockrate)) + return ARRAY_SIZE(kCodecPrefs) - i; + } + LOG(LS_WARNING) << "Unexpected codec \"" << name << "/" << clockrate << "\""; + return -1; +} + +bool WebRtcVoiceEngine::FindChannelAndSsrc( + int channel_num, WebRtcVoiceMediaChannel** channel, uint32* ssrc) const { + ASSERT(channel != NULL && ssrc != NULL); + + *channel = NULL; + *ssrc = 0; + // Find corresponding channel and ssrc + for (ChannelList::const_iterator it = channels_.begin(); + it != channels_.end(); ++it) { + ASSERT(*it != NULL); + if ((*it)->FindSsrc(channel_num, ssrc)) { + *channel = *it; + return true; + } + } + + return false; +} + +void WebRtcVoiceEngine::RegisterChannel(WebRtcVoiceMediaChannel *channel) { + talk_base::CritScope lock(&channels_cs_); + channels_.push_back(channel); +} + +void WebRtcVoiceEngine::UnregisterChannel(WebRtcVoiceMediaChannel *channel) { + talk_base::CritScope lock(&channels_cs_); + ChannelList::iterator i = std::find(channels_.begin(), + channels_.end(), + channel); + if (i != channels_.end()) { + channels_.erase(i); + } +} + +void WebRtcVoiceEngine::RegisterSoundclip(WebRtcSoundclipMedia *soundclip) { + soundclips_.push_back(soundclip); +} + +void WebRtcVoiceEngine::UnregisterSoundclip(WebRtcSoundclipMedia *soundclip) { + SoundclipList::iterator i = std::find(soundclips_.begin(), + soundclips_.end(), + soundclip); + if (i != soundclips_.end()) { + soundclips_.erase(i); + } +} + +// Adjusts the default AGC target level by the specified delta. +// NB: If we start messing with other config fields, we'll want +// to save the current webrtc::AgcConfig as well. +bool WebRtcVoiceEngine::AdjustAgcLevel(int delta) { + webrtc::AgcConfig config = default_agc_config_; + config.targetLeveldBOv += delta; + + LOG(LS_INFO) << "Adjusting AGC level from default -" + << default_agc_config_.targetLeveldBOv << "dB to -" + << config.targetLeveldBOv << "dB"; + + if (voe_wrapper_->processing()->SetAgcConfig(config) == -1) { + LOG_RTCERR1(SetAgcConfig, config.targetLeveldBOv); + return false; + } + return true; +} + +// Configures echo cancellation and noise suppression modes according to +// whether or not we are in a multi-point conference. +bool WebRtcVoiceEngine::SetConferenceMode(bool enable) { +// Only use EC_AECM for mobile. +#if defined(IOS) || defined(ANDROID) + return true; +#endif + + LOG(LS_INFO) << (enable ? "Enabling" : "Disabling") + << " Conference Mode noise reduction"; + + // We always configure noise suppression on, so just toggle the mode. + const webrtc::NsModes ns_mode = enable ? webrtc::kNsConference + : webrtc::kNsDefault; + if (voe_wrapper_->processing()->SetNsStatus(true, ns_mode) == -1) { + LOG_RTCERR2(SetNsStatus, true, ns_mode); + return false; + } + + // Echo-cancellation is a user-option, so preserve the enable state and + // just toggle the mode. + bool aec; + webrtc::EcModes ec_mode; + if (voe_wrapper_->processing()->GetEcStatus(aec, ec_mode) == -1) { + LOG_RTCERR0(GetEcStatus); + return false; + } + ec_mode = enable ? webrtc::kEcConference : webrtc::kEcDefault; + if (voe_wrapper_->processing()->SetEcStatus(aec, ec_mode) == -1) { + LOG_RTCERR2(SetEcStatus, aec, ec_mode); + return false; + } + return true; +} + +// WebRtcVoiceMediaChannel +WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel(WebRtcVoiceEngine *engine) + : WebRtcMediaChannel( + engine, + engine->voe()->base()->CreateChannel()), + channel_options_(0), + agc_adjusted_(false), + dtmf_allowed_(false), + desired_playout_(false), + playout_(false), + desired_send_(SEND_NOTHING), + send_(SEND_NOTHING) { + engine->RegisterChannel(this); + LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel " + << voe_channel(); + + // Register external transport + if (engine->voe()->network()->RegisterExternalTransport( + voe_channel(), *static_cast(this)) == -1) { + LOG_RTCERR2(RegisterExternalTransport, voe_channel(), this); + } + + // Enable RTCP (for quality stats and feedback messages) + EnableRtcp(voe_channel()); + + // Create a random but nonzero send SSRC + SetSendSsrc(talk_base::CreateRandomNonZeroId()); +} + +WebRtcVoiceMediaChannel::~WebRtcVoiceMediaChannel() { + LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::~WebRtcVoiceMediaChannel " + << voe_channel(); + + // DeRegister external transport + if (engine()->voe()->network()->DeRegisterExternalTransport( + voe_channel()) == -1) { + LOG_RTCERR1(DeRegisterExternalTransport, voe_channel()); + } + + // Unregister ourselves from the engine. + engine()->UnregisterChannel(this); + // Remove any remaining streams. + while (!mux_channels_.empty()) { + RemoveStream(mux_channels_.begin()->first); + } + // Delete the primary channel. + if (engine()->voe()->base()->DeleteChannel(voe_channel()) == -1) { + LOG_RTCERR1(DeleteChannel, voe_channel()); + } +} + +bool WebRtcVoiceMediaChannel::SetOptions(int flags) { + // Always accept flags that are unchanged. + if (channel_options_ == flags) { + return true; + } + + // Reject new options if we're already sending. + if (send_ != SEND_NOTHING) { + return false; + } + + // Save the options, to be interpreted where appropriate. + channel_options_ = flags; + return true; +} + +bool WebRtcVoiceMediaChannel::SetRecvCodecs( + const std::vector& codecs) { + // Update our receive payload types to match what we offered. This only is + // an issue when a different entity (i.e. a server) is generating the offer + // for us. + bool ret = true; + for (std::vector::const_iterator i = codecs.begin(); + i != codecs.end() && ret; ++i) { + webrtc::CodecInst gcodec; + if (engine()->FindWebRtcCodec(*i, &gcodec)) { + if (gcodec.pltype != i->id) { + LOG(LS_INFO) << "Updating payload type for " << gcodec.plname + << " from " << gcodec.pltype << " to " << i->id; + gcodec.pltype = i->id; + if (engine()->voe()->codec()->SetRecPayloadType( + voe_channel(), gcodec) == -1) { + LOG_RTCERR1(SetRecPayloadType, voe_channel()); + ret = false; + } + } + } else { + LOG(LS_WARNING) << "Unknown codec " << i->name; + ret = false; + } + } + + return ret; +} + +bool WebRtcVoiceMediaChannel::SetSendCodecs( + const std::vector& codecs) { + // Disable DTMF, VAD, and FEC unless we know the other side wants them. + dtmf_allowed_ = false; + engine()->voe()->codec()->SetVADStatus(voe_channel(), false); + engine()->voe()->rtp()->SetFECStatus(voe_channel(), false); + + // Scan through the list to figure out the codec to use for sending, along + // with the proper configuration for VAD and DTMF. + bool first = true; + webrtc::CodecInst send_codec; + memset(&send_codec, 0, sizeof(send_codec)); + + for (std::vector::const_iterator i = codecs.begin(); + i != codecs.end(); ++i) { + // Ignore codecs we don't know about. The negotiation step should prevent + // this, but double-check to be sure. + webrtc::CodecInst gcodec; + if (!engine()->FindWebRtcCodec(*i, &gcodec)) { + LOG(LS_WARNING) << "Unknown codec " << i->name; + continue; + } + + // Find the DTMF telephone event "codec" and tell VoiceEngine about it. + if (i->name == "telephone-event" || i->name == "audio/telephone-event") { + engine()->voe()->dtmf()->SetSendTelephoneEventPayloadType( + voe_channel(), i->id); + dtmf_allowed_ = true; + } + + // Turn voice activity detection/comfort noise on if supported. + // Set the wideband CN payload type appropriately. + // (narrowband always uses the static payload type 13). + if (i->name == "CN") { + webrtc::PayloadFrequencies cn_freq; + switch (i->clockrate) { + case 8000: + cn_freq = webrtc::kFreq8000Hz; + break; + case 16000: + cn_freq = webrtc::kFreq16000Hz; + break; + case 32000: + cn_freq = webrtc::kFreq32000Hz; + break; + default: + LOG(LS_WARNING) << "CN frequency " << i->clockrate + << " not supported."; + continue; + } + engine()->voe()->codec()->SetVADStatus(voe_channel(), true); + if (cn_freq != webrtc::kFreq8000Hz) { + engine()->voe()->codec()->SetSendCNPayloadType(voe_channel(), + i->id, cn_freq); + } + } + + // We'll use the first codec in the list to actually send audio data. + // Be sure to use the payload type requested by the remote side. + // "red", for FEC audio, is a special case where the actual codec to be + // used is specified in params. + if (first) { + if (i->name == "red") { + // Parse out the RED parameters. If we fail, just ignore RED; + // we don't support all possible params/usage scenarios. + if (!GetRedSendCodec(*i, codecs, &send_codec)) { + continue; + } + + // Enable redundant encoding of the specified codec. Treat any + // failure as a fatal internal error. + LOG(LS_INFO) << "Enabling RED"; + if (engine()->voe()->rtp()->SetFECStatus(voe_channel(), + true, i->id) == -1) { + LOG_RTCERR3(SetFECStatus, voe_channel(), true, i->id); + return false; + } + } else { + send_codec = gcodec; + send_codec.pltype = i->id; + } + first = false; + } + } + + // If we're being asked to set an empty list of codecs, due to a buggy client, + // choose the most common format: PCMU + if (first) { + LOG(LS_WARNING) << "Received empty list of codecs; using PCMU/8000"; + AudioCodec codec(0, "PCMU", 8000, 0, 1, 0); + engine()->FindWebRtcCodec(codec, &send_codec); + } + + // Set the codec. + LOG(LS_INFO) << "Selected voice codec " << send_codec.plname + << "/" << send_codec.plfreq; + if (engine()->voe()->codec()->SetSendCodec(voe_channel(), + send_codec) == -1) { + LOG_RTCERR1(SetSendCodec, voe_channel()); + return false; + } + + return true; +} + +bool WebRtcVoiceMediaChannel::SetRecvRtpHeaderExtensions( + const std::vector& extensions) { + // We don't support any incoming extensions headers right now. + return true; +} + +bool WebRtcVoiceMediaChannel::SetSendRtpHeaderExtensions( + const std::vector& extensions) { + // Enable the audio level extension header if requested. + std::vector::const_iterator it; + for (it = extensions.begin(); it != extensions.end(); ++it) { + if (it->uri == kRtpAudioLevelHeaderExtension) { + break; + } + } + + bool enable = (it != extensions.end()); + int id = 0; + + if (enable) { + id = it->id; + if (id < kMinRtpHeaderExtensionId || + id > kMaxRtpHeaderExtensionId) { + LOG(LS_WARNING) << "Invalid RTP header extension id " << id; + return false; + } + } + +// This api call is not available in iOS version of VoiceEngine currently. +#if !defined(IOS) && !defined(ANDROID) + if (engine()->voe()->rtp()->SetRTPAudioLevelIndicationStatus( + voe_channel(), enable, id) == -1) { + LOG_RTCERR3(SetRTPAudioLevelIndicationStatus, voe_channel(), enable, id); + return false; + } +#endif + + return true; +} + +bool WebRtcVoiceMediaChannel::SetPlayout(bool playout) { + desired_playout_ = playout; + return ChangePlayout(desired_playout_); +} + +bool WebRtcVoiceMediaChannel::PausePlayout() { + return ChangePlayout(false); +} + +bool WebRtcVoiceMediaChannel::ResumePlayout() { + return ChangePlayout(desired_playout_); +} + +bool WebRtcVoiceMediaChannel::ChangePlayout(bool playout) { + if (playout_ == playout) { + return true; + } + + bool result = true; + if (mux_channels_.empty()) { + // Only toggle the default channel if we don't have any other channels. + result = SetPlayout(voe_channel(), playout); + } + for (ChannelMap::iterator it = mux_channels_.begin(); + it != mux_channels_.end() && result; ++it) { + if (!SetPlayout(it->second, playout)) { + LOG(LS_ERROR) << "SetPlayout " << playout << " on channel " << it->second + << " failed"; + result = false; + } + } + + if (result) { + playout_ = playout; + } + return result; +} + +bool WebRtcVoiceMediaChannel::SetSend(SendFlags send) { + desired_send_ = send; + return ChangeSend(desired_send_); +} + +bool WebRtcVoiceMediaChannel::PauseSend() { + return ChangeSend(SEND_NOTHING); +} + +bool WebRtcVoiceMediaChannel::ResumeSend() { + return ChangeSend(desired_send_); +} + +bool WebRtcVoiceMediaChannel::ChangeSend(SendFlags send) { + if (send_ == send) { + return true; + } + + if (send == SEND_MICROPHONE) { +#ifdef CHROMEOS + // Conference mode doesn't work well on ChromeOS. + if (!engine()->SetConferenceMode(false)) { + LOG_RTCERR1(SetConferenceMode, voe_channel()); + return false; + } +#else + // Multi-point conferences use conference-mode noise filtering. + if (!engine()->SetConferenceMode( + 0 != (channel_options_ & OPT_CONFERENCE))) { + LOG_RTCERR1(SetConferenceMode, voe_channel()); + return false; + } +#endif // CHROMEOS + + // Tandberg-bridged conferences have an AGC target that is lower than + // GTV-only levels. + // TODO(ronghuawu): replace 0x80000000 with OPT_AGC_TANDBERG_LEVELS + if ((channel_options_ & 0x80000000) && !agc_adjusted_) { + if (engine()->AdjustAgcLevel(kTandbergDbAdjustment)) { + agc_adjusted_ = true; + } + } + + // VoiceEngine resets sequence number when StopSend is called. This + // sometimes causes libSRTP to complain about packets being + // replayed. To get around this we store the last sent sequence + // number and initializes the channel with the next to continue on + // the same sequence. + if (sequence_number() != -1) { + LOG(LS_INFO) << "WebRtcVoiceMediaChannel restores seqnum=" + << sequence_number() + 1; + if (engine()->voe()->sync()->SetInitSequenceNumber( + voe_channel(), sequence_number() + 1) == -1) { + LOG_RTCERR2(SetInitSequenceNumber, voe_channel(), + sequence_number() + 1); + } + } + if (engine()->voe()->base()->StartSend(voe_channel()) == -1) { + LOG_RTCERR1(StartSend, voe_channel()); + return false; + } + if (engine()->voe()->file()->StopPlayingFileAsMicrophone( + voe_channel()) == -1) { + LOG_RTCERR1(StopPlayingFileAsMicrophone, voe_channel()); + return false; + } + } else if (send == SEND_RINGBACKTONE) { + ASSERT(ringback_tone_.get() != NULL); + if (!ringback_tone_.get()) { + return false; + } + if (engine()->voe()->file()->StartPlayingFileAsMicrophone( + voe_channel(), ringback_tone_.get(), false) == -1) { + LOG_RTCERR3(StartPlayingFileAsMicrophone, voe_channel(), + ringback_tone_.get(), false); + return false; + } + // VoiceEngine resets sequence number when StopSend is called. This + // sometimes causes libSRTP to complain about packets being + // replayed. To get around this we store the last sent sequence + // number and initializes the channel with the next to continue on + // the same sequence. + if (sequence_number() != -1) { + LOG(LS_INFO) << "WebRtcVoiceMediaChannel restores seqnum=" + << sequence_number() + 1; + if (engine()->voe()->sync()->SetInitSequenceNumber( + voe_channel(), sequence_number() + 1) == -1) { + LOG_RTCERR2(SetInitSequenceNumber, voe_channel(), + sequence_number() + 1); + } + } + if (engine()->voe()->base()->StartSend(voe_channel()) == -1) { + LOG_RTCERR1(StartSend, voe_channel()); + return false; + } + } else { // SEND_NOTHING + if (engine()->voe()->base()->StopSend(voe_channel()) == -1) { + LOG_RTCERR1(StopSend, voe_channel()); + } + + // Reset the AGC level, if it was set. + if (agc_adjusted_) { + if (engine()->AdjustAgcLevel(0)) { + agc_adjusted_ = false; + } + } + + // Disable conference-mode noise filtering. + if (!engine()->SetConferenceMode(false)) { + LOG_RTCERR1(SetConferenceMode, voe_channel()); + } + } + send_ = send; + return true; +} + +bool WebRtcVoiceMediaChannel::AddStream(uint32 ssrc) { + talk_base::CritScope lock(&mux_channels_cs_); + + if (mux_channels_.find(ssrc) != mux_channels_.end()) { + return false; + } + + // Create a new channel for receiving audio data. + int channel = engine()->voe()->base()->CreateChannel(); + if (channel == -1) { + LOG_RTCERR0(CreateChannel); + return false; + } + + // Configure to use external transport, like our default channel. + if (engine()->voe()->network()->RegisterExternalTransport( + channel, *this) == -1) { + LOG_RTCERR2(SetExternalTransport, channel, this); + return false; + } + + // Use the same SSRC as our default channel (so the RTCP reports are correct). + unsigned int send_ssrc; + webrtc::VoERTP_RTCP* rtp = engine()->voe()->rtp(); + if (rtp->GetLocalSSRC(voe_channel(), send_ssrc) == -1) { + LOG_RTCERR2(GetSendSSRC, channel, send_ssrc); + return false; + } + if (rtp->SetLocalSSRC(channel, send_ssrc) == -1) { + LOG_RTCERR2(SetSendSSRC, channel, send_ssrc); + return false; + } + + if (mux_channels_.empty() && playout_) { + // This is the first stream in a multi user meeting. We can now + // disable playback of the default stream. This since the default + // stream will probably have received some initial packets before + // the new stream was added. This will mean that the CN state from + // the default channel will be mixed in with the other streams + // throughout the whole meeting, which might be disturbing. + LOG(LS_INFO) << "Disabling playback on the default voice channel"; + SetPlayout(voe_channel(), false); + } + + mux_channels_[ssrc] = channel; + + // TODO(juberti): We should rollback the add if SetPlayout fails. + LOG(LS_INFO) << "New audio stream " << ssrc + << " registered to VoiceEngine channel #" + << channel << "."; + return SetPlayout(channel, playout_); +} + +bool WebRtcVoiceMediaChannel::RemoveStream(uint32 ssrc) { + talk_base::CritScope lock(&mux_channels_cs_); + ChannelMap::iterator it = mux_channels_.find(ssrc); + + if (it != mux_channels_.end()) { + if (engine()->voe()->network()->DeRegisterExternalTransport( + it->second) == -1) { + LOG_RTCERR1(DeRegisterExternalTransport, it->second); + } + + LOG(LS_INFO) << "Removing audio stream " << ssrc + << " with VoiceEngine channel #" + << it->second << "."; + if (engine()->voe()->base()->DeleteChannel(it->second) == -1) { + LOG_RTCERR1(DeleteChannel, voe_channel()); + return false; + } + + mux_channels_.erase(it); + if (mux_channels_.empty() && playout_) { + // The last stream was removed. We can now enable the default + // channel for new channels to be played out immediately without + // waiting for AddStream messages. + // TODO(oja): Does the default channel still have it's CN state? + LOG(LS_INFO) << "Enabling playback on the default voice channel"; + SetPlayout(voe_channel(), true); + } + } + return true; +} + +bool WebRtcVoiceMediaChannel::GetActiveStreams( + AudioInfo::StreamList* actives) { + actives->clear(); + for (ChannelMap::iterator it = mux_channels_.begin(); + it != mux_channels_.end(); ++it) { + int level = GetOutputLevel(it->second); + if (level > 0) { + actives->push_back(std::make_pair(it->first, level)); + } + } + return true; +} + +int WebRtcVoiceMediaChannel::GetOutputLevel() { + // return the highest output level of all streams + int highest = GetOutputLevel(voe_channel()); + for (ChannelMap::iterator it = mux_channels_.begin(); + it != mux_channels_.end(); ++it) { + int level = GetOutputLevel(it->second); + highest = talk_base::_max(level, highest); + } + return highest; +} + +bool WebRtcVoiceMediaChannel::SetRingbackTone(const char *buf, int len) { + ringback_tone_.reset(new WebRtcSoundclipStream(buf, len)); + return true; +} + +bool WebRtcVoiceMediaChannel::PlayRingbackTone(uint32 ssrc, + bool play, bool loop) { + if (!ringback_tone_.get()) { + return false; + } + + // Determine which VoiceEngine channel to play on. + int channel = (ssrc == 0) ? voe_channel() : GetChannel(ssrc); + if (channel == -1) { + return false; + } + + // Make sure the ringtone is cued properly, and play it out. + if (play) { + ringback_tone_->set_loop(loop); + ringback_tone_->Rewind(); + if (engine()->voe()->file()->StartPlayingFileLocally(channel, + ringback_tone_.get()) == -1) { + LOG_RTCERR2(StartPlayingFileLocally, channel, ringback_tone_.get()); + LOG(LS_ERROR) << "Unable to start ringback tone"; + return false; + } + ringback_channels_.insert(channel); + LOG(LS_INFO) << "Started ringback on channel " << channel; + } else { + if (engine()->voe()->file()->StopPlayingFileLocally(channel) + == -1) { + LOG_RTCERR1(StopPlayingFileLocally, channel); + return false; + } + LOG(LS_INFO) << "Stopped ringback on channel " << channel; + ringback_channels_.erase(channel); + } + + return true; +} + +bool WebRtcVoiceMediaChannel::PressDTMF(int event, bool playout) { + if (!dtmf_allowed_) { + return false; + } + + // Enable or disable DTMF playout of this tone as requested. This will linger + // until the next call to this method, but that's OK. + if (engine()->voe()->dtmf()->SetDtmfFeedbackStatus(playout) == -1) { + LOG_RTCERR2(SendDTMF, voe_channel(), playout); + return false; + } + + // Send DTMF using out-of-band DTMF. ("true", as 3rd arg) + if (engine()->voe()->dtmf()->SendTelephoneEvent(voe_channel(), event, + true) == -1) { + LOG_RTCERR3(SendDTMF, voe_channel(), event, true); + return false; + } + + return true; +} + +void WebRtcVoiceMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { + // Pick which channel to send this packet to. If this packet doesn't match + // any multiplexed streams, just send it to the default channel. Otherwise, + // send it to the specific decoder instance for that stream. + int which_channel = GetChannel( + ParseSsrc(packet->data(), packet->length(), false)); + if (which_channel == -1) { + which_channel = voe_channel(); + } + + // Stop any ringback that might be playing on the channel. + // It's possible the ringback has already stopped, ih which case we'll just + // use the opportunity to remove the channel from ringback_channels_. + const std::set::iterator it = ringback_channels_.find(which_channel); + if (it != ringback_channels_.end()) { + if (engine()->voe()->file()->IsPlayingFileLocally( + which_channel) == 1) { + engine()->voe()->file()->StopPlayingFileLocally(which_channel); + LOG(LS_INFO) << "Stopped ringback on channel " << which_channel + << " due to incoming media"; + } + ringback_channels_.erase(which_channel); + } + + // Pass it off to the decoder. + engine()->voe()->network()->ReceivedRTPPacket(which_channel, + packet->data(), + packet->length()); +} + +void WebRtcVoiceMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) { + // See above. + int which_channel = GetChannel( + ParseSsrc(packet->data(), packet->length(), true)); + if (which_channel == -1) { + which_channel = voe_channel(); + } + + engine()->voe()->network()->ReceivedRTCPPacket(which_channel, + packet->data(), + packet->length()); +} + +void WebRtcVoiceMediaChannel::SetSendSsrc(uint32 ssrc) { + if (engine()->voe()->rtp()->SetLocalSSRC(voe_channel(), ssrc) + == -1) { + LOG_RTCERR2(SetSendSSRC, voe_channel(), ssrc); + } +} + +bool WebRtcVoiceMediaChannel::SetRtcpCName(const std::string& cname) { + if (engine()->voe()->rtp()->SetRTCP_CNAME(voe_channel(), + cname.c_str()) == -1) { + LOG_RTCERR2(SetRTCP_CNAME, voe_channel(), cname); + return false; + } + return true; +} + +bool WebRtcVoiceMediaChannel::Mute(bool muted) { + if (engine()->voe()->volume()->SetInputMute(voe_channel(), + muted) == -1) { + LOG_RTCERR2(SetInputMute, voe_channel(), muted); + return false; + } + return true; +} + +bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { + // In VoiceEngine 3.5, GetRTCPStatistics will return 0 even when it fails, + // causing the stats to contain garbage information. To prevent this, we + // zero the stats structure before calling this API. + // TODO(juberti): Remove this workaround. + webrtc::CallStatistics cs; + unsigned int ssrc; + webrtc::CodecInst codec; + unsigned int level; + + // Fill in the sender info, based on what we know, and what the + // remote side told us it got from its RTCP report. + VoiceSenderInfo sinfo; + memset(&sinfo, 0, sizeof(sinfo)); + + // Data we obtain locally. + memset(&cs, 0, sizeof(cs)); + if (engine()->voe()->rtp()->GetRTCPStatistics(voe_channel(), cs) == -1 || + engine()->voe()->rtp()->GetLocalSSRC(voe_channel(), ssrc) == -1) { + return false; + } + + sinfo.ssrc = ssrc; + sinfo.bytes_sent = cs.bytesSent; + sinfo.packets_sent = cs.packetsSent; + // RTT isn't known until a RTCP report is received. Until then, VoiceEngine + // returns 0 to indicate an error value. + sinfo.rtt_ms = (cs.rttMs > 0) ? cs.rttMs : -1; + + // Data from the last remote RTCP report. + unsigned int ntp_high, ntp_low, timestamp, ptimestamp, jitter; + unsigned short loss; // NOLINT + if (engine()->voe()->rtp()->GetRemoteRTCPData(voe_channel(), + ntp_high, ntp_low, timestamp, ptimestamp, &jitter, &loss) != -1 && + engine()->voe()->codec()->GetSendCodec(voe_channel(), + codec) != -1) { + // Convert Q8 to floating point. + sinfo.fraction_lost = static_cast(loss) / (1 << 8); + // Convert samples to milliseconds. + if (codec.plfreq / 1000 > 0) { + sinfo.jitter_ms = jitter / (codec.plfreq / 1000); + } + } else { + sinfo.fraction_lost = -1; + sinfo.jitter_ms = -1; + } + // TODO(juberti): Figure out how to get remote packets_lost, ext_seqnum + sinfo.packets_lost = -1; + sinfo.ext_seqnum = -1; + + // Local speech level. + sinfo.audio_level = (engine()->voe()->volume()-> + GetSpeechInputLevelFullRange(level) != -1) ? level : -1; + info->senders.push_back(sinfo); + + // Build the list of receivers, one for each mux channel, or 1 in a 1:1 call. + std::vector channels; + for (ChannelMap::const_iterator it = mux_channels_.begin(); + it != mux_channels_.end(); ++it) { + channels.push_back(it->second); + } + if (channels.empty()) { + channels.push_back(voe_channel()); + } + + // Get the SSRC and stats for each receiver, based on our own calculations. + for (std::vector::const_iterator it = channels.begin(); + it != channels.end(); ++it) { + memset(&cs, 0, sizeof(cs)); + if (engine()->voe()->rtp()->GetRemoteSSRC(*it, ssrc) != -1 && + engine()->voe()->rtp()->GetRTCPStatistics(*it, cs) != -1 && + engine()->voe()->codec()->GetRecCodec(*it, codec) != -1) { + VoiceReceiverInfo rinfo; + memset(&rinfo, 0, sizeof(rinfo)); + rinfo.ssrc = ssrc; + rinfo.bytes_rcvd = cs.bytesReceived; + rinfo.packets_rcvd = cs.packetsReceived; + // The next four fields are from the most recently sent RTCP report. + // Convert Q8 to floating point. + rinfo.fraction_lost = static_cast(cs.fractionLost) / (1 << 8); + rinfo.packets_lost = cs.cumulativeLost; + rinfo.ext_seqnum = cs.extendedMax; + // Convert samples to milliseconds. + if (codec.plfreq / 1000 > 0) { + rinfo.jitter_ms = cs.jitterSamples / (codec.plfreq / 1000); + } + + // Get jitter buffer and total delay (alg + jitter + playout) stats. + webrtc::NetworkStatistics ns; + if (engine()->voe()->neteq() && + engine()->voe()->neteq()->GetNetworkStatistics( + *it, ns) != -1) { + rinfo.jitter_buffer_ms = ns.currentBufferSize; + rinfo.jitter_buffer_preferred_ms = ns.preferredBufferSize; + } + if (engine()->voe()->sync()) { + engine()->voe()->sync()->GetDelayEstimate(*it, + rinfo.delay_estimate_ms); + } + + // Get speech level. + rinfo.audio_level = (engine()->voe()->volume()-> + GetSpeechOutputLevelFullRange(*it, level) != -1) ? level : -1; + info->receivers.push_back(rinfo); + } + } + + return true; +} + +void WebRtcVoiceMediaChannel::GetLastMediaError( + uint32* ssrc, VoiceMediaChannel::Error* error) { + ASSERT(ssrc != NULL); + ASSERT(error != NULL); + FindSsrc(voe_channel(), ssrc); + *error = WebRtcErrorToChannelError(GetLastEngineError()); +} + +bool WebRtcVoiceMediaChannel::FindSsrc(int channel_num, uint32* ssrc) { + talk_base::CritScope lock(&mux_channels_cs_); + ASSERT(ssrc != NULL); + if (channel_num == voe_channel()) { + unsigned local_ssrc = 0; + // This is a sending channel. + if (engine()->voe()->rtp()->GetLocalSSRC( + channel_num, local_ssrc) != -1) { + *ssrc = local_ssrc; + } + return true; + } else if (channel_num == -1 && send_ != SEND_NOTHING) { + // Sometimes the VoiceEngine core will throw error with channel_num = -1. + // This means the error is not limited to a specific channel. Signal the + // message using ssrc=0. If the current channel is sending, use this + // channel for sending the message. + *ssrc = 0; + return true; + } else { + // Check whether this is a receiving channel. + for (ChannelMap::const_iterator it = mux_channels_.begin(); + it != mux_channels_.end(); ++it) { + if (it->second == channel_num) { + *ssrc = it->first; + return true; + } + } + } + return false; +} + +void WebRtcVoiceMediaChannel::OnError(uint32 ssrc, int error) { + SignalMediaError(ssrc, WebRtcErrorToChannelError(error)); +} + +int WebRtcVoiceMediaChannel::GetOutputLevel(int channel) { + unsigned int ulevel; + int ret = + engine()->voe()->volume()->GetSpeechOutputLevel(channel, ulevel); + return (ret == 0) ? static_cast(ulevel) : -1; +} + +int WebRtcVoiceMediaChannel::GetChannel(uint32 ssrc) { + ChannelMap::iterator it = mux_channels_.find(ssrc); + return (it != mux_channels_.end()) ? it->second : -1; +} + +bool WebRtcVoiceMediaChannel::GetRedSendCodec(const AudioCodec& red_codec, + const std::vector& all_codecs, webrtc::CodecInst* send_codec) { + // Get the RED encodings from the parameter with no name. This may + // change based on what is discussed on the Jingle list. + // The encoding parameter is of the form "a/b"; we only support where + // a == b. Verify this and parse out the value into red_pt. + // If the parameter value is absent (as it will be until we wire up the + // signaling of this message), use the second codec specified (i.e. the + // one after "red") as the encoding parameter. + int red_pt = -1; + std::string red_params; + CodecParameterMap::const_iterator it = red_codec.params.find(""); + if (it != red_codec.params.end()) { + red_params = it->second; + std::vector red_pts; + if (talk_base::split(red_params, '/', &red_pts) != 2 || + red_pts[0] != red_pts[1] || + !talk_base::FromString(red_pts[0], &red_pt)) { + LOG(LS_WARNING) << "RED params " << red_params << " not supported."; + return false; + } + } else if (red_codec.params.empty()) { + LOG(LS_WARNING) << "RED params not present, using defaults"; + if (all_codecs.size() > 1) { + red_pt = all_codecs[1].id; + } + } + + // Try to find red_pt in |codecs|. + std::vector::const_iterator codec; + for (codec = all_codecs.begin(); codec != all_codecs.end(); ++codec) { + if (codec->id == red_pt) + break; + } + + // If we find the right codec, that will be the codec we pass to + // SetSendCodec, with the desired payload type. + if (codec != all_codecs.end() && + engine()->FindWebRtcCodec(*codec, send_codec)) { + send_codec->pltype = red_pt; + } else { + LOG(LS_WARNING) << "RED params " << red_params << " are invalid."; + return false; + } + + return true; +} + +bool WebRtcVoiceMediaChannel::EnableRtcp(int channel) { + if (engine()->voe()->rtp()->SetRTCPStatus(channel, true) == -1) { + LOG_RTCERR2(SetRTCPStatus, voe_channel(), 1); + return false; + } + // TODO(juberti): Enable VQMon and RTCP XR reports, once we know what + // what we want to do with them. + // engine()->voe().EnableVQMon(voe_channel(), true); + // engine()->voe().EnableRTCP_XR(voe_channel(), true); + return true; +} + +bool WebRtcVoiceMediaChannel::SetPlayout(int channel, bool playout) { + if (playout) { + LOG(LS_INFO) << "Starting playout for channel #" << channel; + if (engine()->voe()->base()->StartPlayout(channel) == -1) { + LOG_RTCERR1(StartPlayout, channel); + return false; + } + } else { + LOG(LS_INFO) << "Stopping playout for channel #" << channel; + engine()->voe()->base()->StopPlayout(channel); + } + return true; +} + +uint32 WebRtcVoiceMediaChannel::ParseSsrc(const void* data, size_t len, + bool rtcp) { + size_t ssrc_pos = (!rtcp) ? 8 : 4; + uint32 ssrc = 0; + if (len >= (ssrc_pos + sizeof(ssrc))) { + ssrc = talk_base::GetBE32(static_cast(data) + ssrc_pos); + } + return ssrc; +} + +// Convert VoiceEngine error code into VoiceMediaChannel::Error enum. +VoiceMediaChannel::Error + WebRtcVoiceMediaChannel::WebRtcErrorToChannelError(int err_code) { + switch (err_code) { + case 0: + return ERROR_NONE; + case VE_CANNOT_START_RECORDING: + case VE_MIC_VOL_ERROR: + case VE_GET_MIC_VOL_ERROR: + case VE_CANNOT_ACCESS_MIC_VOL: + return ERROR_REC_DEVICE_OPEN_FAILED; + case VE_SATURATION_WARNING: + return ERROR_REC_DEVICE_SATURATION; + case VE_REC_DEVICE_REMOVED: + return ERROR_REC_DEVICE_REMOVED; + case VE_RUNTIME_REC_WARNING: + case VE_RUNTIME_REC_ERROR: + return ERROR_REC_RUNTIME_ERROR; + case VE_CANNOT_START_PLAYOUT: + case VE_SPEAKER_VOL_ERROR: + case VE_GET_SPEAKER_VOL_ERROR: + case VE_CANNOT_ACCESS_SPEAKER_VOL: + return ERROR_PLAY_DEVICE_OPEN_FAILED; + case VE_RUNTIME_PLAY_WARNING: + case VE_RUNTIME_PLAY_ERROR: + return ERROR_PLAY_RUNTIME_ERROR; + case VE_TYPING_NOISE_WARNING: + return ERROR_REC_TYPING_NOISE_DETECTED; + default: + return VoiceMediaChannel::ERROR_OTHER; + } +} + +int WebRtcSoundclipStream::Read(void *buf, int len) { + size_t res = 0; + mem_.Read(buf, len, &res, NULL); + return res; +} + +int WebRtcSoundclipStream::Rewind() { + mem_.Rewind(); + // Return -1 to keep VoiceEngine from looping. + return (loop_) ? 0 : -1; +} + +} // namespace cricket + +#endif // HAVE_WEBRTC diff --git a/third_party_mods/libjingle/source/talk/session/phone/webrtcvoiceengine.h b/third_party_mods/libjingle/source/talk/session/phone/webrtcvoiceengine.h new file mode 100644 index 000000000..d978c91d2 --- /dev/null +++ b/third_party_mods/libjingle/source/talk/session/phone/webrtcvoiceengine.h @@ -0,0 +1,320 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_SESSION_PHONE_WEBRTCVOICEENGINE_H_ +#define TALK_SESSION_PHONE_WEBRTCVOICEENGINE_H_ + +#include +#include +#include +#include + +#include "talk/base/buffer.h" +#include "talk/base/byteorder.h" +#include "talk/base/logging.h" +#include "talk/base/scoped_ptr.h" +#include "talk/base/stream.h" +#include "talk/session/phone/channel.h" +#include "talk/session/phone/mediaengine.h" +#include "talk/session/phone/rtputils.h" +#include "talk/session/phone/webrtccommon.h" + +namespace cricket { + +// WebRtcSoundclipStream is an adapter object that allows a memory stream to be +// passed into WebRtc, and support looping. +class WebRtcSoundclipStream : public webrtc::InStream { + public: + WebRtcSoundclipStream(const char* buf, size_t len) + : mem_(buf, len), loop_(true) { + } + void set_loop(bool loop) { loop_ = loop; } + virtual int Read(void* buf, int len); + virtual int Rewind(); + + private: + talk_base::MemoryStream mem_; + bool loop_; +}; + +// WebRtcMonitorStream is used to monitor a stream coming from WebRtc. +// For now we just dump the data. +class WebRtcMonitorStream : public webrtc::OutStream { + virtual bool Write(const void *buf, int len) { + return true; + } +}; + +class AudioDeviceModule; +class VoETraceWrapper; +class VoEWrapper; +class WebRtcSoundclipMedia; +class WebRtcVoiceMediaChannel; + +// WebRtcVoiceEngine is a class to be used with CompositeMediaEngine. +// It uses the WebRtc VoiceEngine library for audio handling. +class WebRtcVoiceEngine + : public webrtc::VoiceEngineObserver, + public webrtc::TraceCallback { + public: + WebRtcVoiceEngine(); + WebRtcVoiceEngine(webrtc::AudioDeviceModule* adm, + webrtc::AudioDeviceModule* adm_sc); + // Dependency injection for testing. + WebRtcVoiceEngine(VoEWrapper* voe_wrapper, + VoEWrapper* voe_wrapper_sc, + VoETraceWrapper* tracing); + ~WebRtcVoiceEngine(); + bool Init(); + void Terminate(); + + int GetCapabilities(); + VoiceMediaChannel* CreateChannel(); + + SoundclipMedia* CreateSoundclip(); + + bool SetOptions(int options); + bool SetDevices(const Device* in_device, const Device* out_device); + bool GetOutputVolume(int* level); + bool SetOutputVolume(int level); + int GetInputLevel(); + bool SetLocalMonitor(bool enable); + + const std::vector& codecs(); + bool FindCodec(const AudioCodec& codec); + bool FindWebRtcCodec(const AudioCodec& codec, webrtc::CodecInst* gcodec); + + void SetLogging(int min_sev, const char* filter); + + // For tracking WebRtc channels. Needed because we have to pause them + // all when switching devices. + // May only be called by WebRtcVoiceMediaChannel. + void RegisterChannel(WebRtcVoiceMediaChannel *channel); + void UnregisterChannel(WebRtcVoiceMediaChannel *channel); + + // May only be called by WebRtcSoundclipMedia. + void RegisterSoundclip(WebRtcSoundclipMedia *channel); + void UnregisterSoundclip(WebRtcSoundclipMedia *channel); + + // Called by WebRtcVoiceMediaChannel to set a gain offset from + // the default AGC target level. + bool AdjustAgcLevel(int delta); + + // Called by WebRtcVoiceMediaChannel to configure echo cancellation + // and noise suppression modes. + bool SetConferenceMode(bool enable); + + VoEWrapper* voe() { return voe_wrapper_.get(); } + VoEWrapper* voe_sc() { return voe_wrapper_sc_.get(); } + int GetLastEngineError(); + + private: + typedef std::vector SoundclipList; + typedef std::vector ChannelList; + + struct CodecPref { + const char* name; + int clockrate; + }; + + void Construct(); + bool InitInternal(); + void ApplyLogging(); + virtual void Print(const webrtc::TraceLevel level, + const char* trace_string, const int length); + virtual void CallbackOnError(const int channel, const int errCode); + static int GetCodecPreference(const char *name, int clockrate); + // Given the device type, name, and id, find device id. Return true and + // set the output parameter rtc_id if successful. + bool FindWebRtcAudioDeviceId( + bool is_input, const std::string& dev_name, int dev_id, int* rtc_id); + bool FindChannelAndSsrc(int channel_num, + WebRtcVoiceMediaChannel** channel, + uint32* ssrc) const; + bool ChangeLocalMonitor(bool enable); + bool PauseLocalMonitor(); + bool ResumeLocalMonitor(); + + static const int kDefaultLogSeverity = talk_base::LS_WARNING; + static const CodecPref kCodecPrefs[]; + + // The primary instance of WebRtc VoiceEngine. + talk_base::scoped_ptr voe_wrapper_; + // A secondary instance, for playing out soundclips (on the 'ring' device). + talk_base::scoped_ptr voe_wrapper_sc_; + talk_base::scoped_ptr tracing_; + // The external audio device manager + webrtc::AudioDeviceModule* adm_; + webrtc::AudioDeviceModule* adm_sc_; + int log_level_; + bool is_dumping_aec_; + std::vector codecs_; + bool desired_local_monitor_enable_; + talk_base::scoped_ptr monitor_; + SoundclipList soundclips_; + ChannelList channels_; + // channels_ can be read from WebRtc callback thread. We need a lock on that + // callback as well as the RegisterChannel/UnregisterChannel. + talk_base::CriticalSection channels_cs_; + webrtc::AgcConfig default_agc_config_; +}; + +// WebRtcMediaChannel is a class that implements the common WebRtc channel +// functionality. +template +class WebRtcMediaChannel : public T, public webrtc::Transport { + public: + WebRtcMediaChannel(E *engine, int channel) + : engine_(engine), voe_channel_(channel), sequence_number_(-1) {} + E *engine() { return engine_; } + int voe_channel() const { return voe_channel_; } + bool valid() const { return voe_channel_ != -1; } + + protected: + // implements Transport interface + virtual int SendPacket(int channel, const void *data, int len) { + if (!T::network_interface_) { + return -1; + } + + // We need to store the sequence number to be able to pick up + // the same sequence when the device is restarted. + // TODO(oja): Remove when WebRtc has fixed the problem. + int seq_num; + if (!GetRtpSeqNum(data, len, &seq_num)) { + return -1; + } + if (sequence_number() == -1) { + LOG(INFO) << "WebRtcVoiceMediaChannel sends first packet seqnum=" + << seq_num; + } + sequence_number_ = seq_num; + + talk_base::Buffer packet(data, len, kMaxRtpPacketLen); + return T::network_interface_->SendPacket(&packet) ? len : -1; + } + virtual int SendRTCPPacket(int channel, const void *data, int len) { + if (!T::network_interface_) { + return -1; + } + + talk_base::Buffer packet(data, len, kMaxRtpPacketLen); + return T::network_interface_->SendRtcp(&packet) ? len : -1; + } + int sequence_number() const { + return sequence_number_; + } + + private: + E *engine_; + int voe_channel_; + int sequence_number_; +}; + +// WebRtcVoiceMediaChannel is an implementation of VoiceMediaChannel that uses +// WebRtc Voice Engine. +class WebRtcVoiceMediaChannel + : public WebRtcMediaChannel { + public: + explicit WebRtcVoiceMediaChannel(WebRtcVoiceEngine *engine); + virtual ~WebRtcVoiceMediaChannel(); + virtual bool SetOptions(int options); + virtual bool SetRecvCodecs(const std::vector &codecs); + virtual bool SetSendCodecs(const std::vector &codecs); + virtual bool SetRecvRtpHeaderExtensions( + const std::vector& extensions); + virtual bool SetSendRtpHeaderExtensions( + const std::vector& extensions); + virtual bool SetPlayout(bool playout); + bool PausePlayout(); + bool ResumePlayout(); + virtual bool SetSend(SendFlags send); + bool PauseSend(); + bool ResumeSend(); + virtual bool AddStream(uint32 ssrc); + virtual bool RemoveStream(uint32 ssrc); + virtual bool GetActiveStreams(AudioInfo::StreamList* actives); + virtual int GetOutputLevel(); + + virtual bool SetRingbackTone(const char *buf, int len); + virtual bool PlayRingbackTone(uint32 ssrc, bool play, bool loop); + virtual bool PressDTMF(int event, bool playout); + + virtual void OnPacketReceived(talk_base::Buffer* packet); + virtual void OnRtcpReceived(talk_base::Buffer* packet); + virtual void SetSendSsrc(uint32 id); + virtual bool SetRtcpCName(const std::string& cname); + virtual bool Mute(bool mute); + virtual bool SetSendBandwidth(bool autobw, int bps) { return false; } + virtual bool GetStats(VoiceMediaInfo* info); + // Gets last reported error from WebRtc voice engine. This should be only + // called in response a failure. + virtual void GetLastMediaError(uint32* ssrc, + VoiceMediaChannel::Error* error); + bool FindSsrc(int channel_num, uint32* ssrc); + void OnError(uint32 ssrc, int error); + + protected: + int GetLastEngineError() { return engine()->GetLastEngineError(); } + int GetChannel(uint32 ssrc); + int GetOutputLevel(int channel); + bool GetRedSendCodec(const AudioCodec& red_codec, + const std::vector& all_codecs, + webrtc::CodecInst* send_codec); + bool EnableRtcp(int channel); + bool SetPlayout(int channel, bool playout); + static uint32 ParseSsrc(const void* data, size_t len, bool rtcp); + static Error WebRtcErrorToChannelError(int err_code); + + private: + // Tandberg-bridged conferences require a -10dB gain adjustment, + // which is actually +10 in AgcConfig.targetLeveldBOv + static const int kTandbergDbAdjustment = 10; + + bool ChangePlayout(bool playout); + bool ChangeSend(SendFlags send); + + typedef std::map ChannelMap; + talk_base::scoped_ptr ringback_tone_; + std::set ringback_channels_; // channels playing ringback + int channel_options_; + bool agc_adjusted_; + bool dtmf_allowed_; + bool desired_playout_; + bool playout_; + SendFlags desired_send_; + SendFlags send_; + ChannelMap mux_channels_; // for multiple sources + // mux_channels_ can be read from WebRtc callback thread. Accesses off the + // WebRtc thread must be synchronized with edits on the worker thread. Reads + // on the worker thread are ok. + mutable talk_base::CriticalSection mux_channels_cs_; +}; +} + +#endif // TALK_SESSION_PHONE_WEBRTCVOICEENGINE_H_