* Update to use libjingle r85.
* Remove (most of) local libjingle mods. Only webrtcvideoengine and webrtcvoiceengine are left now, because the refcounted module has not yet been released to libjingle, so I can't submit the changes to libjingle at the moment. * Update the peerconnection client sample app. Review URL: http://webrtc-codereview.appspot.com/151004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@625 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
86b85db67e
commit
cb99f78653
2
DEPS
2
DEPS
@ -4,7 +4,7 @@ vars = {
|
||||
"googlecode_url": "http://%s.googlecode.com/svn",
|
||||
"chromium_trunk" : "http://src.chromium.org/svn/trunk",
|
||||
"chromium_revision": "98568",
|
||||
"libjingle_revision": "77",
|
||||
"libjingle_revision": "85",
|
||||
}
|
||||
|
||||
# NOTE: Prefer revision numbers to tags.
|
||||
|
@ -69,8 +69,7 @@ bool Conductor::InitializePeerConnection() {
|
||||
talk_base::SocketAddress());
|
||||
|
||||
peer_connection_factory_.reset(
|
||||
new webrtc::PeerConnectionFactory(GetPeerConnectionString(),
|
||||
port_allocator,
|
||||
new webrtc::PeerConnectionFactory(port_allocator,
|
||||
worker_thread_.get()));
|
||||
if (!peer_connection_factory_->Initialize()) {
|
||||
main_wnd_->MessageBox("Error",
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "talk/base/win32.h"
|
||||
#include "talk/session/phone/mediachannel.h"
|
||||
#include "talk/session/phone/videocommon.h"
|
||||
#include "talk/session/phone/videoframe.h"
|
||||
#include "talk/session/phone/videorenderer.h"
|
||||
|
||||
class MainWndCallback {
|
||||
public:
|
||||
|
@ -24,8 +24,11 @@
|
||||
'_USE_32BIT_TIME_T',
|
||||
'SAFE_TO_DEFINE_TALK_BASE_LOGGING_MACROS',
|
||||
'EXPAT_RELATIVE_PATH',
|
||||
'JSONCPP_RELATIVE_PATH',
|
||||
'WEBRTC_RELATIVE_PATH',
|
||||
'HAVE_WEBRTC',
|
||||
'HAVE_WEBRTC_VIDEO',
|
||||
'HAVE_WEBRTC_VOICE',
|
||||
],
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
@ -109,7 +112,6 @@
|
||||
['inside_chromium_build==1', {
|
||||
'include_dirs': [
|
||||
'<(overrides)',
|
||||
'<(libjingle_mods)/source',
|
||||
'<(libjingle_orig)/source',
|
||||
'../..', # the third_party folder for webrtc includes
|
||||
'../../third_party/expat/files',
|
||||
@ -117,7 +119,6 @@
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'<(overrides)',
|
||||
'<(libjingle_mods)/source',
|
||||
'<(libjingle_orig)/source',
|
||||
'../../third_party/expat/files'
|
||||
],
|
||||
@ -130,7 +131,6 @@
|
||||
'include_dirs': [
|
||||
# the third_party folder for webrtc/ includes (non-chromium).
|
||||
'../../src',
|
||||
'<(libjingle_mods)/source',
|
||||
'<(libjingle_orig)/source',
|
||||
'../../third_party/expat/files',
|
||||
],
|
||||
@ -270,8 +270,8 @@
|
||||
'<(libjingle_orig)/source/talk/base/httpcommon.h',
|
||||
'<(libjingle_orig)/source/talk/base/httprequest.cc',
|
||||
'<(libjingle_orig)/source/talk/base/httprequest.h',
|
||||
'<(libjingle_mods)/source/talk/base/json.cc',
|
||||
'<(libjingle_mods)/source/talk/base/json.h',
|
||||
'<(libjingle_orig)/source/talk/base/json.cc',
|
||||
'<(libjingle_orig)/source/talk/base/json.h',
|
||||
'<(libjingle_orig)/source/talk/base/linked_ptr.h',
|
||||
'<(libjingle_orig)/source/talk/base/logging.cc',
|
||||
'<(libjingle_orig)/source/talk/base/md5.h',
|
||||
@ -336,6 +336,21 @@
|
||||
'<(libjingle_orig)/source/talk/base/time.h',
|
||||
'<(libjingle_orig)/source/talk/base/urlencode.cc',
|
||||
'<(libjingle_orig)/source/talk/base/urlencode.h',
|
||||
'<(libjingle_orig)/source/talk/base/worker.cc',
|
||||
'<(libjingle_orig)/source/talk/base/worker.h',
|
||||
'<(libjingle_orig)/source/talk/sound/automaticallychosensoundsystem.h',
|
||||
'<(libjingle_orig)/source/talk/sound/platformsoundsystem.cc',
|
||||
'<(libjingle_orig)/source/talk/sound/platformsoundsystem.h',
|
||||
'<(libjingle_orig)/source/talk/sound/platformsoundsystemfactory.cc',
|
||||
'<(libjingle_orig)/source/talk/sound/platformsoundsystemfactory.h',
|
||||
'<(libjingle_orig)/source/talk/sound/sounddevicelocator.h',
|
||||
'<(libjingle_orig)/source/talk/sound/soundinputstreaminterface.h',
|
||||
'<(libjingle_orig)/source/talk/sound/soundoutputstreaminterface.h',
|
||||
'<(libjingle_orig)/source/talk/sound/soundsystemfactory.h',
|
||||
'<(libjingle_orig)/source/talk/sound/soundsysteminterface.cc',
|
||||
'<(libjingle_orig)/source/talk/sound/soundsysteminterface.h',
|
||||
'<(libjingle_orig)/source/talk/sound/soundsystemproxy.cc',
|
||||
'<(libjingle_orig)/source/talk/sound/soundsystemproxy.h',
|
||||
'<(libjingle_orig)/source/talk/xmllite/xmlbuilder.cc',
|
||||
'<(libjingle_orig)/source/talk/xmllite/xmlbuilder.h',
|
||||
'<(libjingle_orig)/source/talk/xmllite/xmlconstants.cc',
|
||||
@ -409,6 +424,16 @@
|
||||
'sources': [
|
||||
'<(libjingle_orig)/source/talk/base/linux.cc',
|
||||
'<(libjingle_orig)/source/talk/base/linux.h',
|
||||
'<(libjingle_orig)/source/talk/sound/alsasoundsystem.cc',
|
||||
'<(libjingle_orig)/source/talk/sound/alsasoundsystem.h',
|
||||
'<(libjingle_orig)/source/talk/sound/alsasymboltable.cc',
|
||||
'<(libjingle_orig)/source/talk/sound/alsasymboltable.h',
|
||||
'<(libjingle_orig)/source/talk/sound/linuxsoundsystem.cc',
|
||||
'<(libjingle_orig)/source/talk/sound/linuxsoundsystem.h',
|
||||
'<(libjingle_orig)/source/talk/sound/pulseaudiosoundsystem.cc',
|
||||
'<(libjingle_orig)/source/talk/sound/pulseaudiosoundsystem.h',
|
||||
'<(libjingle_orig)/source/talk/sound/pulseaudiosymboltable.cc',
|
||||
'<(libjingle_orig)/source/talk/sound/pulseaudiosymboltable.h',
|
||||
],
|
||||
}],
|
||||
['OS=="mac"', {
|
||||
@ -494,7 +519,7 @@
|
||||
'<(libjingle_orig)/source/talk/session/phone/call.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/channel.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/channel.h',
|
||||
'<(libjingle_mods)/source/talk/session/phone/channelmanager.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/channelmanager.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/channelmanager.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/codec.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/codec.h',
|
||||
@ -502,7 +527,7 @@
|
||||
'<(libjingle_orig)/source/talk/session/phone/currentspeakermonitor.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/currentspeakermonitor.h',
|
||||
'<(libjingle_mods)/source/talk/session/phone/devicemanager.cc',
|
||||
'<(libjingle_mods)/source/talk/session/phone/devicemanager.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/devicemanager.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/filemediaengine.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/filemediaengine.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/mediachannel.h',
|
||||
@ -512,6 +537,7 @@
|
||||
'<(libjingle_orig)/source/talk/session/phone/mediamessages.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/mediamonitor.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/mediamonitor.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/mediasession.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/mediasessionclient.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/mediasessionclient.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/mediasink.h',
|
||||
@ -523,19 +549,20 @@
|
||||
'<(libjingle_orig)/source/talk/session/phone/rtputils.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/soundclip.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/soundclip.h',
|
||||
'<(libjingle_mods)/source/talk/session/phone/srtpfilter.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/srtpfilter.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/srtpfilter.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/videocommon.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/webrtcpassthroughrender.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/voicechannel.h',
|
||||
'<(libjingle_mods)/source/talk/session/phone/webrtccommon.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/webrtccommon.h',
|
||||
'<(libjingle_mods)/source/talk/session/phone/webrtcvideoengine.cc',
|
||||
'<(libjingle_mods)/source/talk/session/phone/webrtcvideoengine.h',
|
||||
'<(libjingle_mods)/source/talk/session/phone/webrtcvideoframe.cc',
|
||||
'<(libjingle_mods)/source/talk/session/phone/webrtcvideoframe.h',
|
||||
'<(libjingle_mods)/source/talk/session/phone/webrtcvie.h',
|
||||
'<(libjingle_mods)/source/talk/session/phone/webrtcvoe.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/webrtcvideoengine.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/webrtcvideoframe.cc',
|
||||
'<(libjingle_orig)/source/talk/session/phone/webrtcvideoframe.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/webrtcvie.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/webrtcvoe.h',
|
||||
'<(libjingle_mods)/source/talk/session/phone/webrtcvoiceengine.cc',
|
||||
'<(libjingle_mods)/source/talk/session/phone/webrtcvoiceengine.h',
|
||||
'<(libjingle_orig)/source/talk/session/phone/webrtcvoiceengine.h',
|
||||
'<(libjingle_orig)/source/talk/session/tunnel/pseudotcpchannel.cc',
|
||||
'<(libjingle_orig)/source/talk/session/tunnel/pseudotcpchannel.h',
|
||||
'<(libjingle_orig)/source/talk/session/tunnel/tunnelsessionclient.cc',
|
||||
@ -592,21 +619,17 @@
|
||||
'target_name': 'libjingle_app',
|
||||
'type': '<(library)',
|
||||
'sources': [
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/peerconnection.h',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/peerconnectionfactory.h',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/peerconnectionfactory.cc',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/peerconnection_impl.cc',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/peerconnection_impl.h',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/peerconnection_proxy.cc',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/peerconnection_proxy.h',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/webrtcdevicemanager.cc',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/webrtcdevicemanager.h',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/webrtcmediaengine.cc',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/webrtcmediaengine.h',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/webrtcsession.cc',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/webrtcsession.h',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/webrtc_json.cc',
|
||||
'<(libjingle_mods)/source/talk/app/webrtc/webrtc_json.h',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/peerconnection.h',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/peerconnectionfactory.h',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/peerconnectionfactory.cc',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/peerconnectionimpl.cc',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/peerconnectionimpl.h',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/peerconnectionproxy.cc',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/peerconnectionproxy.h',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/webrtcsession.cc',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/webrtcsession.h',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/webrtcjson.cc',
|
||||
'<(libjingle_orig)/source/talk/app/webrtc/webrtcjson.h',
|
||||
],
|
||||
'dependencies': [
|
||||
'jsoncpp',
|
||||
|
@ -1,134 +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_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 <string>
|
||||
|
||||
namespace cricket {
|
||||
class VideoRenderer;
|
||||
}
|
||||
|
||||
namespace talk_base {
|
||||
class Thread;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
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, 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:
|
||||
enum ReadyState {
|
||||
NEW = 0,
|
||||
NEGOTIATING,
|
||||
ACTIVE,
|
||||
CLOSED,
|
||||
};
|
||||
|
||||
virtual ~PeerConnection() {}
|
||||
|
||||
// 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 bool 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;
|
||||
|
||||
// Returns the state of the PeerConnection object. See the ReadyState
|
||||
// enum for valid values.
|
||||
virtual ReadyState GetReadyState() = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // TALK_APP_WEBRTC_PEERCONNECTION_H_
|
@ -1,224 +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/webrtc/peerconnection_impl.h"
|
||||
|
||||
#include "talk/app/webrtc/webrtc_json.h"
|
||||
#include "talk/app/webrtc/webrtcsession.h"
|
||||
#include "talk/base/basicpacketsocketfactory.h"
|
||||
#include "talk/base/helpers.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/stringencode.h"
|
||||
#include "talk/p2p/base/session.h"
|
||||
#include "talk/p2p/client/basicportallocator.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
||||
PeerConnectionImpl::PeerConnectionImpl(
|
||||
cricket::PortAllocator* port_allocator,
|
||||
cricket::ChannelManager* channel_manager,
|
||||
talk_base::Thread* signaling_thread)
|
||||
: port_allocator_(port_allocator),
|
||||
channel_manager_(channel_manager),
|
||||
signaling_thread_(signaling_thread),
|
||||
event_callback_(NULL),
|
||||
session_(NULL) {
|
||||
}
|
||||
|
||||
PeerConnectionImpl::~PeerConnectionImpl() {
|
||||
}
|
||||
|
||||
bool PeerConnectionImpl::Init() {
|
||||
std::string sid;
|
||||
talk_base::CreateRandomString(8, &sid);
|
||||
const bool incoming = false; // default outgoing direction
|
||||
session_.reset(CreateMediaSession(sid, incoming));
|
||||
if (session_.get() == NULL) {
|
||||
ASSERT(false && "failed to initialize a session");
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
// Deserialize signaling message
|
||||
cricket::SessionDescription* incoming_sdp = NULL;
|
||||
std::vector<cricket::Candidate> candidates;
|
||||
if (!ParseJSONSignalingMessage(signaling_message,
|
||||
incoming_sdp, &candidates)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
if (GetReadyState() == NEW) {
|
||||
// set direction to incoming, as message received first
|
||||
session_->set_incoming(true);
|
||||
ret = session_->OnInitiateMessage(incoming_sdp, candidates);
|
||||
} else {
|
||||
ret = session_->OnRemoteDescription(incoming_sdp, candidates);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
WebRtcSession* PeerConnectionImpl::CreateMediaSession(
|
||||
const std::string& id, bool incoming) {
|
||||
ASSERT(port_allocator_ != NULL);
|
||||
WebRtcSession* session = new WebRtcSession(id, incoming,
|
||||
port_allocator_, channel_manager_, signaling_thread_);
|
||||
|
||||
if (session->Initiate()) {
|
||||
session->SignalAddStream.connect(
|
||||
this,
|
||||
&PeerConnectionImpl::OnAddStream);
|
||||
session->SignalRemoveStream.connect(
|
||||
this,
|
||||
&PeerConnectionImpl::OnRemoveStream);
|
||||
session->SignalLocalDescription.connect(
|
||||
this,
|
||||
&PeerConnectionImpl::OnLocalDescription);
|
||||
session->SignalFailedCall.connect(
|
||||
this,
|
||||
&PeerConnectionImpl::OnFailedCall);
|
||||
} else {
|
||||
delete session;
|
||||
session = NULL;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
bool PeerConnectionImpl::AddStream(const std::string& stream_id, bool video) {
|
||||
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) {
|
||||
return session_->RemoveStream(stream_id);
|
||||
}
|
||||
|
||||
void PeerConnectionImpl::OnLocalDescription(
|
||||
const cricket::SessionDescription* desc,
|
||||
const std::vector<cricket::Candidate>& 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) {
|
||||
return channel_manager_->SetAudioOptions(wave_in_device,
|
||||
wave_out_device,
|
||||
opts);
|
||||
}
|
||||
|
||||
bool PeerConnectionImpl::SetLocalVideoRenderer(
|
||||
cricket::VideoRenderer* renderer) {
|
||||
return channel_manager_->SetLocalRenderer(renderer);
|
||||
}
|
||||
|
||||
bool PeerConnectionImpl::SetVideoRenderer(const std::string& stream_id,
|
||||
cricket::VideoRenderer* renderer) {
|
||||
return session_->SetVideoRenderer(stream_id, renderer);
|
||||
}
|
||||
|
||||
bool PeerConnectionImpl::SetVideoCapture(const std::string& cam_device) {
|
||||
return channel_manager_->SetVideoOptions(cam_device);
|
||||
}
|
||||
|
||||
bool PeerConnectionImpl::Connect() {
|
||||
return session_->Connect();
|
||||
}
|
||||
|
||||
// TODO(mallinath) - Close is not used anymore, should be removed.
|
||||
bool PeerConnectionImpl::Close() {
|
||||
session_->RemoveAllStreams();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PeerConnectionImpl::OnAddStream(const std::string& stream_id,
|
||||
bool video) {
|
||||
if (event_callback_) {
|
||||
event_callback_->OnAddStream(stream_id, video);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerConnectionImpl::OnRemoveStream(const std::string& stream_id,
|
||||
bool video) {
|
||||
if (event_callback_) {
|
||||
event_callback_->OnRemoveStream(stream_id, video);
|
||||
}
|
||||
}
|
||||
|
||||
PeerConnectionImpl::ReadyState PeerConnectionImpl::GetReadyState() {
|
||||
ReadyState ready_state;
|
||||
cricket::BaseSession::State state = session_->state();
|
||||
if (state == cricket::BaseSession::STATE_INIT) {
|
||||
ready_state = NEW;
|
||||
} else if (state == cricket::BaseSession::STATE_INPROGRESS) {
|
||||
ready_state = ACTIVE;
|
||||
} else if (state == cricket::BaseSession::STATE_DEINIT) {
|
||||
ready_state = CLOSED;
|
||||
} else {
|
||||
ready_state = NEGOTIATING;
|
||||
}
|
||||
return ready_state;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -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_PEERCONNECTION_IMPL_H_
|
||||
#define TALK_APP_WEBRTC_PEERCONNECTION_IMPL_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "talk/app/webrtc/peerconnection.h"
|
||||
#include "talk/base/sigslot.h"
|
||||
#include "talk/base/scoped_ptr.h"
|
||||
#include "talk/base/thread.h"
|
||||
#include "talk/session/phone/channelmanager.h"
|
||||
|
||||
namespace cricket {
|
||||
class ChannelManager;
|
||||
class PortAllocator;
|
||||
class SessionDescription;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
class WebRtcSession;
|
||||
|
||||
class PeerConnectionImpl : public PeerConnection,
|
||||
public sigslot::has_slots<> {
|
||||
public:
|
||||
PeerConnectionImpl(cricket::PortAllocator* port_allocator,
|
||||
cricket::ChannelManager* channel_manager,
|
||||
talk_base::Thread* signaling_thread);
|
||||
virtual ~PeerConnectionImpl();
|
||||
|
||||
// PeerConnection interfaces
|
||||
virtual void RegisterObserver(PeerConnectionObserver* observer);
|
||||
virtual bool SignalingMessage(const std::string& msg);
|
||||
virtual bool AddStream(const std::string& stream_id, bool video);
|
||||
virtual bool RemoveStream(const std::string& stream_id);
|
||||
virtual bool Connect();
|
||||
virtual bool Close();
|
||||
virtual bool SetAudioDevice(const std::string& wave_in_device,
|
||||
const std::string& wave_out_device, int opts);
|
||||
virtual bool SetLocalVideoRenderer(cricket::VideoRenderer* renderer);
|
||||
virtual bool SetVideoRenderer(const std::string& stream_id,
|
||||
cricket::VideoRenderer* renderer);
|
||||
virtual bool SetVideoCapture(const std::string& cam_device);
|
||||
virtual ReadyState GetReadyState();
|
||||
|
||||
cricket::ChannelManager* channel_manager() {
|
||||
return channel_manager_;
|
||||
}
|
||||
|
||||
// Callbacks from PeerConnectionImplCallbacks
|
||||
void OnAddStream(const std::string& stream_id, bool video);
|
||||
void OnRemoveStream(const std::string& stream_id, bool video);
|
||||
void OnLocalDescription(
|
||||
const cricket::SessionDescription* desc,
|
||||
const std::vector<cricket::Candidate>& candidates);
|
||||
void OnFailedCall();
|
||||
bool Init();
|
||||
|
||||
private:
|
||||
bool ParseConfigString(const std::string& config,
|
||||
talk_base::SocketAddress* stun_addr);
|
||||
void SendRemoveSignal(WebRtcSession* session);
|
||||
WebRtcSession* CreateMediaSession(const std::string& id, bool incoming);
|
||||
|
||||
cricket::PortAllocator* port_allocator_;
|
||||
cricket::ChannelManager* channel_manager_;
|
||||
talk_base::Thread* signaling_thread_;
|
||||
PeerConnectionObserver* event_callback_;
|
||||
talk_base::scoped_ptr<WebRtcSession> session_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // TALK_APP_WEBRTC_PEERCONNECTION_IMPL_H_
|
@ -1,313 +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/webrtc/peerconnection_proxy.h"
|
||||
|
||||
#include "talk/app/webrtc/peerconnection_impl.h"
|
||||
#include "talk/base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
enum {
|
||||
MSG_WEBRTC_ADDSTREAM = 1,
|
||||
MSG_WEBRTC_CLOSE,
|
||||
MSG_WEBRTC_CONNECT,
|
||||
MSG_WEBRTC_INIT,
|
||||
MSG_WEBRTC_REGISTEROBSERVER,
|
||||
MSG_WEBRTC_RELEASE,
|
||||
MSG_WEBRTC_REMOVESTREAM,
|
||||
MSG_WEBRTC_SETAUDIODEVICE,
|
||||
MSG_WEBRTC_SETLOCALRENDERER,
|
||||
MSG_WEBRTC_SETVIDEOCAPTURE,
|
||||
MSG_WEBRTC_SETVIDEORENDERER,
|
||||
MSG_WEBRTC_SIGNALINGMESSAGE,
|
||||
MSG_WEBRTC_GETREADYSTATE,
|
||||
};
|
||||
|
||||
struct AddStreamParams : public talk_base::MessageData {
|
||||
AddStreamParams(const std::string& stream_id, bool video)
|
||||
: stream_id(stream_id),
|
||||
video(video),
|
||||
result(false) {}
|
||||
|
||||
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),
|
||||
result(false) {}
|
||||
|
||||
std::string stream_id;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct SignalingMsgParams : public talk_base::MessageData {
|
||||
explicit SignalingMsgParams(const std::string& signaling_message)
|
||||
: signaling_message(signaling_message),
|
||||
result(false) {}
|
||||
|
||||
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), result(false) {}
|
||||
|
||||
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), result(false) {}
|
||||
|
||||
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), result(false) {}
|
||||
|
||||
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), result(false) {}
|
||||
|
||||
std::string cam_device;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct RegisterObserverParams : public talk_base::MessageData {
|
||||
explicit RegisterObserverParams(PeerConnectionObserver* observer)
|
||||
: observer(observer), result(false) {}
|
||||
|
||||
PeerConnectionObserver* observer;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct ResultParams : public talk_base::MessageData {
|
||||
ResultParams()
|
||||
: result(false) {}
|
||||
|
||||
bool result;
|
||||
};
|
||||
|
||||
PeerConnectionProxy::PeerConnectionProxy(
|
||||
cricket::PortAllocator* port_allocator,
|
||||
cricket::ChannelManager* channel_manager,
|
||||
talk_base::Thread* signaling_thread)
|
||||
: peerconnection_impl_(new PeerConnectionImpl(port_allocator,
|
||||
channel_manager, signaling_thread)),
|
||||
signaling_thread_(signaling_thread) {
|
||||
}
|
||||
|
||||
PeerConnectionProxy::~PeerConnectionProxy() {
|
||||
ResultParams params;
|
||||
Send(MSG_WEBRTC_RELEASE, ¶ms);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::Init() {
|
||||
ResultParams params;
|
||||
return (Send(MSG_WEBRTC_INIT, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
void PeerConnectionProxy::RegisterObserver(PeerConnectionObserver* observer) {
|
||||
RegisterObserverParams params(observer);
|
||||
Send(MSG_WEBRTC_REGISTEROBSERVER, ¶ms);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::SignalingMessage(
|
||||
const std::string& signaling_message) {
|
||||
SignalingMsgParams params(signaling_message);
|
||||
return (Send(MSG_WEBRTC_SIGNALINGMESSAGE, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::AddStream(const std::string& stream_id, bool video) {
|
||||
AddStreamParams params(stream_id, video);
|
||||
return (Send(MSG_WEBRTC_ADDSTREAM, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::RemoveStream(const std::string& stream_id) {
|
||||
RemoveStreamParams params(stream_id);
|
||||
return (Send(MSG_WEBRTC_REMOVESTREAM, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::SetAudioDevice(const std::string& wave_in_device,
|
||||
const std::string& wave_out_device,
|
||||
int opts) {
|
||||
SetAudioDeviceParams params(wave_in_device, wave_out_device, opts);
|
||||
return (Send(MSG_WEBRTC_SETAUDIODEVICE, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::SetLocalVideoRenderer(
|
||||
cricket::VideoRenderer* renderer) {
|
||||
SetLocalRendererParams params(renderer);
|
||||
return (Send(MSG_WEBRTC_SETLOCALRENDERER, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::SetVideoRenderer(const std::string& stream_id,
|
||||
cricket::VideoRenderer* renderer) {
|
||||
SetVideoRendererParams params(stream_id, renderer);
|
||||
return (Send(MSG_WEBRTC_SETVIDEORENDERER, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::SetVideoCapture(const std::string& cam_device) {
|
||||
SetVideoCaptureParams params(cam_device);
|
||||
return (Send(MSG_WEBRTC_SETVIDEOCAPTURE, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
PeerConnection::ReadyState PeerConnectionProxy::GetReadyState() {
|
||||
PeerConnection::ReadyState ready_state = NEW;
|
||||
Send(MSG_WEBRTC_GETREADYSTATE,
|
||||
reinterpret_cast<talk_base::MessageData*>(&ready_state));
|
||||
return ready_state;
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::Connect() {
|
||||
ResultParams params;
|
||||
return (Send(MSG_WEBRTC_CONNECT, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::Close() {
|
||||
ResultParams params;
|
||||
return (Send(MSG_WEBRTC_CLOSE, ¶ms) && params.result);
|
||||
}
|
||||
|
||||
bool PeerConnectionProxy::Send(uint32 id, talk_base::MessageData* data) {
|
||||
if (!signaling_thread_)
|
||||
return false;
|
||||
signaling_thread_->Send(this, id, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PeerConnectionProxy::OnMessage(talk_base::Message* message) {
|
||||
talk_base::MessageData* data = message->pdata;
|
||||
switch (message->message_id) {
|
||||
case MSG_WEBRTC_ADDSTREAM: {
|
||||
AddStreamParams* params = reinterpret_cast<AddStreamParams*>(data);
|
||||
params->result = peerconnection_impl_->AddStream(
|
||||
params->stream_id, params->video);
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_SIGNALINGMESSAGE: {
|
||||
SignalingMsgParams* params =
|
||||
reinterpret_cast<SignalingMsgParams*>(data);
|
||||
params->result = peerconnection_impl_->SignalingMessage(
|
||||
params->signaling_message);
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_REMOVESTREAM: {
|
||||
RemoveStreamParams* params = reinterpret_cast<RemoveStreamParams*>(data);
|
||||
params->result = peerconnection_impl_->RemoveStream(
|
||||
params->stream_id);
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_SETAUDIODEVICE: {
|
||||
SetAudioDeviceParams* params =
|
||||
reinterpret_cast<SetAudioDeviceParams*>(data);
|
||||
params->result = peerconnection_impl_->SetAudioDevice(
|
||||
params->wave_in_device, params->wave_out_device, params->opts);
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_SETLOCALRENDERER: {
|
||||
SetLocalRendererParams* params =
|
||||
reinterpret_cast<SetLocalRendererParams*>(data);
|
||||
params->result = peerconnection_impl_->SetLocalVideoRenderer(
|
||||
params->renderer);
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_SETVIDEOCAPTURE: {
|
||||
SetVideoCaptureParams* params =
|
||||
reinterpret_cast<SetVideoCaptureParams*>(data);
|
||||
params->result = peerconnection_impl_->SetVideoCapture(
|
||||
params->cam_device);
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_GETREADYSTATE: {
|
||||
PeerConnection::ReadyState* ready_state =
|
||||
reinterpret_cast<PeerConnection::ReadyState*>(data);
|
||||
*ready_state = peerconnection_impl_->GetReadyState();
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_SETVIDEORENDERER: {
|
||||
SetVideoRendererParams* params =
|
||||
reinterpret_cast<SetVideoRendererParams*>(data);
|
||||
params->result = peerconnection_impl_->SetVideoRenderer(
|
||||
params->stream_id, params->renderer);
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_CONNECT: {
|
||||
ResultParams* params =
|
||||
reinterpret_cast<ResultParams*>(data);
|
||||
params->result = peerconnection_impl_->Connect();
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_CLOSE: {
|
||||
ResultParams* params =
|
||||
reinterpret_cast<ResultParams*>(data);
|
||||
params->result = peerconnection_impl_->Close();
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_INIT: {
|
||||
ResultParams* params =
|
||||
reinterpret_cast<ResultParams*>(data);
|
||||
params->result = peerconnection_impl_->Init();
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_REGISTEROBSERVER: {
|
||||
RegisterObserverParams* params =
|
||||
reinterpret_cast<RegisterObserverParams*>(data);
|
||||
peerconnection_impl_->RegisterObserver(params->observer);
|
||||
break;
|
||||
}
|
||||
case MSG_WEBRTC_RELEASE: {
|
||||
peerconnection_impl_.reset();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -1,82 +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_PEERCONNECTION_PROXY_H_
|
||||
#define TALK_APP_WEBRTC_PEERCONNECTION_PROXY_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "talk/app/webrtc/peerconnection.h"
|
||||
#include "talk/base/scoped_ptr.h"
|
||||
#include "talk/base/thread.h"
|
||||
|
||||
namespace cricket {
|
||||
class ChannelManager;
|
||||
class PortAllocator;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class PeerConnectionImpl;
|
||||
|
||||
class PeerConnectionProxy : public PeerConnection,
|
||||
public talk_base::MessageHandler {
|
||||
public:
|
||||
PeerConnectionProxy(cricket::PortAllocator* port_allocator,
|
||||
cricket::ChannelManager* channel_manager,
|
||||
talk_base::Thread* signaling_thread);
|
||||
virtual ~PeerConnectionProxy();
|
||||
|
||||
// PeerConnection interface implementation.
|
||||
virtual void RegisterObserver(PeerConnectionObserver* observer);
|
||||
virtual bool SignalingMessage(const std::string& msg);
|
||||
virtual bool AddStream(const std::string& stream_id, bool video);
|
||||
virtual bool RemoveStream(const std::string& stream_id);
|
||||
virtual bool Connect();
|
||||
virtual bool Close();
|
||||
virtual bool SetAudioDevice(const std::string& wave_in_device,
|
||||
const std::string& wave_out_device, int opts);
|
||||
virtual bool SetLocalVideoRenderer(cricket::VideoRenderer* renderer);
|
||||
virtual bool SetVideoRenderer(const std::string& stream_id,
|
||||
cricket::VideoRenderer* renderer);
|
||||
virtual bool SetVideoCapture(const std::string& cam_device);
|
||||
virtual ReadyState GetReadyState();
|
||||
|
||||
private:
|
||||
|
||||
bool Init();
|
||||
bool Send(uint32 id, talk_base::MessageData* data);
|
||||
virtual void OnMessage(talk_base::Message* message);
|
||||
|
||||
talk_base::scoped_ptr<PeerConnectionImpl> peerconnection_impl_;
|
||||
talk_base::Thread* signaling_thread_;
|
||||
|
||||
friend class PeerConnectionFactory;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TALK_APP_WEBRTC_PEERCONNECTION_PROXY_H_
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 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 "base/gunit.h"
|
||||
|
||||
TEST(PeerConnectionTest, InitializationReceiveSanity) {
|
||||
// TODO(henrike): implement.
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
#include "talk/app/webrtc/peerconnectionfactory.h"
|
||||
|
||||
#include "talk/app/webrtc/peerconnection_proxy.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/p2p/client/basicportallocator.h"
|
||||
#include "talk/session/phone/channelmanager.h"
|
||||
|
||||
namespace {
|
||||
// The number of the tokens in the config string.
|
||||
static const size_t kConfigTokens = 2;
|
||||
// The default stun port.
|
||||
static const int kDefaultStunPort = 3478;
|
||||
// NOTE: Must be in the same order as the enum.
|
||||
static const char* kValidServiceTypes[
|
||||
webrtc::PeerConnectionFactory::SERVICE_COUNT] = {
|
||||
"STUN", "STUNS", "TURN", "TURNS" };
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
PeerConnectionFactory::PeerConnectionFactory(
|
||||
const std::string& config,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
cricket::MediaEngine* media_engine,
|
||||
cricket::DeviceManager* device_manager,
|
||||
talk_base::Thread* worker_thread)
|
||||
: config_(config),
|
||||
initialized_(false),
|
||||
port_allocator_(port_allocator),
|
||||
channel_manager_(new cricket::ChannelManager(
|
||||
media_engine, device_manager, worker_thread)) {
|
||||
}
|
||||
|
||||
PeerConnectionFactory::PeerConnectionFactory(
|
||||
const std::string& config,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
talk_base::Thread* worker_thread)
|
||||
: config_(config),
|
||||
initialized_(false),
|
||||
port_allocator_(port_allocator),
|
||||
channel_manager_(new cricket::ChannelManager(worker_thread)) {
|
||||
}
|
||||
|
||||
PeerConnectionFactory::~PeerConnectionFactory() {
|
||||
}
|
||||
|
||||
bool PeerConnectionFactory::Initialize() {
|
||||
ASSERT(channel_manager_.get());
|
||||
std::vector<talk_base::SocketAddress> stun_hosts;
|
||||
talk_base::SocketAddress stun_addr;
|
||||
if (!ParseConfigString(config_, &stun_addr))
|
||||
return false;
|
||||
stun_hosts.push_back(stun_addr);
|
||||
|
||||
initialized_ = channel_manager_->Init();
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
PeerConnection* PeerConnectionFactory::CreatePeerConnection(
|
||||
talk_base::Thread* signaling_thread) {
|
||||
PeerConnectionProxy* pc = NULL;
|
||||
if (initialized_) {
|
||||
pc = new PeerConnectionProxy(
|
||||
port_allocator_.get(), channel_manager_.get(), signaling_thread);
|
||||
if (!pc->Init()) {
|
||||
LOG(LERROR) << "Error in initializing PeerConnection";
|
||||
delete pc;
|
||||
pc = NULL;
|
||||
}
|
||||
} else {
|
||||
LOG(LERROR) << "PeerConnectionFactory is not initialize";
|
||||
}
|
||||
return pc;
|
||||
}
|
||||
|
||||
bool PeerConnectionFactory::ParseConfigString(
|
||||
const std::string& config, talk_base::SocketAddress* stun_addr) {
|
||||
std::vector<std::string> tokens;
|
||||
talk_base::tokenize(config_, ' ', &tokens);
|
||||
|
||||
if (tokens.size() != kConfigTokens) {
|
||||
LOG(WARNING) << "Invalid config string";
|
||||
return false;
|
||||
}
|
||||
|
||||
service_type_ = INVALID;
|
||||
|
||||
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<ServiceType>(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;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -1,64 +0,0 @@
|
||||
#ifndef TALK_APP_WEBRTC_PEERCONNECTIONFACTORY_H_
|
||||
#define TALK_APP_WEBRTC_PEERCONNECTIONFACTORY_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "talk/base/scoped_ptr.h"
|
||||
|
||||
namespace cricket {
|
||||
class ChannelManager;
|
||||
class DeviceManager;
|
||||
class MediaEngine;
|
||||
class PortAllocator;
|
||||
} // namespace cricket
|
||||
|
||||
namespace talk_base {
|
||||
class SocketAddress;
|
||||
class Thread;
|
||||
} // namespace talk_base
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class PeerConnection;
|
||||
|
||||
class PeerConnectionFactory {
|
||||
public:
|
||||
// NOTE: The order of the enum values must be in sync with the array
|
||||
// in Initialize().
|
||||
enum ServiceType {
|
||||
STUN = 0,
|
||||
STUNS,
|
||||
TURN,
|
||||
TURNS,
|
||||
SERVICE_COUNT,
|
||||
INVALID
|
||||
};
|
||||
|
||||
PeerConnectionFactory(const std::string& config,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
cricket::MediaEngine* media_engine,
|
||||
cricket::DeviceManager* device_manager,
|
||||
talk_base::Thread* worker_thread);
|
||||
PeerConnectionFactory(const std::string& config,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
talk_base::Thread* worker_thread);
|
||||
|
||||
virtual ~PeerConnectionFactory();
|
||||
bool Initialize();
|
||||
|
||||
PeerConnection* CreatePeerConnection(talk_base::Thread* signaling_thread);
|
||||
|
||||
private:
|
||||
bool ParseConfigString(const std::string&, talk_base::SocketAddress*);
|
||||
ServiceType service_type_;
|
||||
std::string config_;
|
||||
bool initialized_;
|
||||
talk_base::scoped_ptr<cricket::PortAllocator> port_allocator_;
|
||||
talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // TALK_APP_WEBRTC_PEERCONNECTIONFACTORY_H_
|
||||
|
@ -1,57 +0,0 @@
|
||||
# -*- Python -*-
|
||||
import talk
|
||||
|
||||
Import('env')
|
||||
|
||||
# local sources
|
||||
talk.Library(
|
||||
env,
|
||||
name = 'webrtc',
|
||||
srcs = [
|
||||
'peerconnection_impl.cc',
|
||||
'peerconnection_proxy.cc',
|
||||
'peerconnectionfactory.cc',
|
||||
'webrtc_json.cc',
|
||||
'webrtcsession.cc',
|
||||
],
|
||||
)
|
||||
|
||||
talk.Unittest(
|
||||
env,
|
||||
name = 'webrtc',
|
||||
srcs = [
|
||||
'webrtcsession_unittest.cc',
|
||||
'peerconnection_unittest.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'
|
||||
],
|
||||
)
|
@ -1,516 +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/webrtc/webrtc_json.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <locale>
|
||||
#include <string>
|
||||
|
||||
#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 {
|
||||
|
||||
// Before [de]serializing, we need to work around a bug in jsoncpp where
|
||||
// locale sensitive string conversion functions are used (e.g. sprintf,
|
||||
// sscanf and stl). The problem is that depending on what the current
|
||||
// locale is, numbers might be formatted differently than the jsoncpp code
|
||||
// otherwise expects. E.g. sprintf might format a number as "1,234" and
|
||||
// the parser assumes that it would be "1.234".
|
||||
class AutoSwitchToClassicLocale {
|
||||
public:
|
||||
AutoSwitchToClassicLocale() {
|
||||
const char* locale_name = setlocale(LC_NUMERIC, NULL);
|
||||
if (locale_name)
|
||||
saved_locale_ = locale_name;
|
||||
|
||||
// Switch the CRT to "C".
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
// Switch STL to classic.
|
||||
cxx_locale_ = std::locale::global(std::locale::classic());
|
||||
}
|
||||
|
||||
~AutoSwitchToClassicLocale() {
|
||||
// Switch the locale back to what it was before.
|
||||
std::locale::global(cxx_locale_);
|
||||
setlocale(LC_NUMERIC, saved_locale_.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string saved_locale_;
|
||||
std::locale cxx_locale_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
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<cricket::Candidate>& candidates,
|
||||
std::string* signaling_message) {
|
||||
// See documentation for AutoSwitchToClassicLocale.
|
||||
AutoSwitchToClassicLocale auto_switch;
|
||||
|
||||
const cricket::ContentInfo* audio_content = GetFirstAudioContent(sdp);
|
||||
const cricket::ContentInfo* video_content = GetFirstVideoContent(sdp);
|
||||
|
||||
std::vector<Json::Value> 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<cricket::Candidate>& candidates,
|
||||
bool video,
|
||||
Json::Value* params) {
|
||||
if (video) {
|
||||
Append(params, "label", 2); // always video 2
|
||||
} else {
|
||||
Append(params, "label", 1); // always audio 1
|
||||
}
|
||||
|
||||
const cricket::MediaContentDescription* media_info =
|
||||
static_cast<const cricket::MediaContentDescription*> (
|
||||
content_info.description);
|
||||
if (media_info->rtcp_mux()) {
|
||||
Append(params, "rtcp_mux", std::string("supported"));
|
||||
}
|
||||
|
||||
std::vector<Json::Value> rtpmap;
|
||||
if (!BuildRtpMapParams(content_info, video, &rtpmap)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Append(params, "rtpmap", rtpmap);
|
||||
|
||||
Json::Value attributes;
|
||||
std::vector<Json::Value> 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<Json::Value>* rtpmap) {
|
||||
if (!video) {
|
||||
const cricket::AudioContentDescription* audio_offer =
|
||||
static_cast<const cricket::AudioContentDescription*>(
|
||||
content_info.description);
|
||||
|
||||
std::vector<cricket::AudioCodec>::const_iterator iter =
|
||||
audio_offer->codecs().begin();
|
||||
std::vector<cricket::AudioCodec>::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<const cricket::VideoContentDescription*>(
|
||||
content_info.description);
|
||||
|
||||
std::vector<cricket::VideoCodec>::const_iterator iter =
|
||||
video_offer->codecs().begin();
|
||||
std::vector<cricket::VideoCodec>::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<cricket::Candidate>& candidates,
|
||||
bool video,
|
||||
std::vector<Json::Value>* jcandidates) {
|
||||
std::vector<cricket::Candidate>::const_iterator iter =
|
||||
candidates.begin();
|
||||
std::vector<cricket::Candidate>::const_iterator iter_end =
|
||||
candidates.end();
|
||||
for (; iter != iter_end; ++iter) {
|
||||
if ((video && (!iter->name().compare("video_rtcp") ||
|
||||
(!iter->name().compare("video_rtp")))) ||
|
||||
(!video && (!iter->name().compare("rtp") ||
|
||||
(!iter->name().compare("rtcp"))))) {
|
||||
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<cricket::Candidate>* candidates) {
|
||||
ASSERT(!sdp); // expect this to be NULL
|
||||
|
||||
// See documentation for AutoSwitchToClassicLocale.
|
||||
AutoSwitchToClassicLocale auto_switch;
|
||||
|
||||
// first deserialize message
|
||||
Json::Value value;
|
||||
if (!Deserialize(signaling_message, &value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get media objects
|
||||
std::vector<Json::Value> 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->set_rtcp_mux(ParseRTCPMux(mlines[i]));
|
||||
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->set_rtcp_mux(ParseRTCPMux(mlines[i]));
|
||||
video_content->SortCodecs();
|
||||
sdp->AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP, video_content);
|
||||
ParseICECandidates(mlines[i], candidates);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseRTCPMux(const Json::Value& value) {
|
||||
Json::Value rtcp_mux(ReadValue(value, "rtcp_mux"));
|
||||
if (!rtcp_mux.empty()) {
|
||||
if (rtcp_mux.asString().compare("supported") == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParseAudioCodec(const Json::Value& value,
|
||||
cricket::AudioContentDescription* content) {
|
||||
std::vector<Json::Value> rtpmap(ReadValues(value, "rtpmap"));
|
||||
if (rtpmap.empty())
|
||||
return false;
|
||||
|
||||
std::vector<Json::Value>::const_iterator iter =
|
||||
rtpmap.begin();
|
||||
std::vector<Json::Value>::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<std::string> 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<Json::Value> rtpmap(ReadValues(value, "rtpmap"));
|
||||
if (rtpmap.empty())
|
||||
return false;
|
||||
|
||||
std::vector<Json::Value>::const_iterator iter =
|
||||
rtpmap.begin();
|
||||
std::vector<Json::Value>::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<std::string> 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<cricket::Candidate>* 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<Json::Value> jcandidates(ReadValues(attributes, "candidate"));
|
||||
|
||||
std::vector<Json::Value>::const_iterator iter =
|
||||
jcandidates.begin();
|
||||
std::vector<Json::Value>::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<Json::Value> ReadValues(
|
||||
const Json::Value& value, const std::string& key) {
|
||||
std::vector<Json::Value> objects;
|
||||
for (Json::ArrayIndex 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, 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<Json::Value>& values) {
|
||||
for (std::vector<Json::Value>::const_iterator iter = values.begin();
|
||||
iter != values.end(); ++iter) {
|
||||
(*object)[key].append(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -1,126 +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_WEBRTC_JSON_H_
|
||||
#define TALK_APP_WEBRTC_WEBRTC_JSON_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef WEBRTC_RELATIVE_PATH
|
||||
#include "json/json.h"
|
||||
#else
|
||||
#include "third_party/jsoncpp/json.h"
|
||||
#endif
|
||||
#include "talk/session/phone/codec.h"
|
||||
#include "talk/p2p/base/candidate.h"
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
class AudioContentDescription;
|
||||
class VideoContentDescription;
|
||||
struct ContentInfo;
|
||||
class SessionDescription;
|
||||
}
|
||||
|
||||
struct StunServiceDetails {
|
||||
std::string host;
|
||||
std::string service;
|
||||
std::string protocol;
|
||||
};
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
bool GetConnectionMediator(const Json::Value& value,
|
||||
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<Json::Value> ReadValues(const Json::Value& value,
|
||||
const std::string& key);
|
||||
|
||||
bool BuildMediaMessage(
|
||||
const cricket::ContentInfo& content_info,
|
||||
const std::vector<cricket::Candidate>& candidates,
|
||||
bool video,
|
||||
Json::Value* value);
|
||||
|
||||
bool GetJSONSignalingMessage(
|
||||
const cricket::SessionDescription* sdp,
|
||||
const std::vector<cricket::Candidate>& candidates,
|
||||
std::string* signaling_message);
|
||||
|
||||
bool BuildRtpMapParams(
|
||||
const cricket::ContentInfo& audio_offer,
|
||||
bool video,
|
||||
std::vector<Json::Value>* rtpmap);
|
||||
|
||||
bool BuildAttributes(const std::vector<cricket::Candidate>& candidates,
|
||||
bool video,
|
||||
std::vector<Json::Value>* 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<cricket::Candidate>* 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<cricket::Candidate>* candidates);
|
||||
bool ParseRTCPMux(const Json::Value& value);
|
||||
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, char * value);
|
||||
void Append(Json::Value* object, const std::string& key, double 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<Json::Value>& values);
|
||||
}
|
||||
|
||||
#endif // TALK_APP_WEBRTC_WEBRTC_JSON_H_
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 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/webrtcdevicemanager.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using cricket::Device;
|
||||
using cricket::DeviceManager;
|
||||
|
||||
const int WebRtcDeviceManager::kDefaultDeviceId = -1;
|
||||
|
||||
WebRtcDeviceManager::WebRtcDeviceManager()
|
||||
: DeviceManager(),
|
||||
default_device_(DeviceManager::kDefaultDeviceName, kDefaultDeviceId) {
|
||||
}
|
||||
|
||||
WebRtcDeviceManager::~WebRtcDeviceManager() {
|
||||
Terminate();
|
||||
}
|
||||
|
||||
bool WebRtcDeviceManager::Init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcDeviceManager::Terminate() {
|
||||
}
|
||||
|
||||
bool WebRtcDeviceManager::GetAudioInputDevices(
|
||||
std::vector<Device>* devs) {
|
||||
return GetDefaultDevices(devs);
|
||||
}
|
||||
|
||||
bool WebRtcDeviceManager::GetAudioOutputDevices(
|
||||
std::vector<Device>* devs) {
|
||||
return GetDefaultDevices(devs);
|
||||
}
|
||||
|
||||
bool WebRtcDeviceManager::GetVideoCaptureDevices(
|
||||
std::vector<Device>* devs) {
|
||||
return GetDefaultDevices(devs);
|
||||
}
|
||||
|
||||
bool WebRtcDeviceManager::GetDefaultVideoCaptureDevice(
|
||||
Device* device) {
|
||||
*device = default_device_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcDeviceManager::GetDefaultDevices(
|
||||
std::vector<cricket::Device>* devs) {
|
||||
if (!devs)
|
||||
return false;
|
||||
devs->clear();
|
||||
devs->push_back(default_device_);
|
||||
return true;
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 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_WEBRTCDEVICEMANAGER_H_
|
||||
#define TALK_APP_WEBRTC_WEBRTCDEVICEMANAGER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "talk/session/phone/devicemanager.h"
|
||||
|
||||
class WebRtcDeviceManager : public cricket::DeviceManager {
|
||||
public:
|
||||
WebRtcDeviceManager();
|
||||
~WebRtcDeviceManager();
|
||||
virtual bool Init();
|
||||
virtual void Terminate();
|
||||
virtual bool GetAudioInputDevices(std::vector<cricket::Device>* devs);
|
||||
virtual bool GetAudioOutputDevices(std::vector<cricket::Device>* devs);
|
||||
virtual bool GetVideoCaptureDevices(std::vector<cricket::Device>* devs);
|
||||
virtual bool GetDefaultVideoCaptureDevice(cricket::Device* device);
|
||||
|
||||
private:
|
||||
static const int kDefaultDeviceId;
|
||||
bool GetDefaultDevices(std::vector<cricket::Device>* devs);
|
||||
|
||||
cricket::Device default_device_;
|
||||
};
|
||||
|
||||
#endif // TALK_APP_WEBRTC_WEBRTCDEVICEMANAGER_H_
|
@ -1,143 +0,0 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 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/webrtcmediaengine.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "talk/session/phone/webrtcvoiceengine.h"
|
||||
#include "talk/session/phone/webrtcvideoengine.h"
|
||||
|
||||
WebRtcMediaEngine::WebRtcMediaEngine(webrtc::AudioDeviceModule* adm,
|
||||
webrtc::AudioDeviceModule* adm_sc, webrtc::VideoCaptureModule* vcm)
|
||||
: voice_(new cricket::WebRtcVoiceEngine(adm, adm_sc)),
|
||||
video_(new cricket::WebRtcVideoEngine(voice_.get(), vcm)) {
|
||||
}
|
||||
|
||||
WebRtcMediaEngine::~WebRtcMediaEngine() {
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::Init() {
|
||||
if (!voice_->Init())
|
||||
return false;
|
||||
if (!video_->Init()) {
|
||||
voice_->Terminate();
|
||||
return false;
|
||||
}
|
||||
SignalVideoCaptureResult.repeat(video_->SignalCaptureResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcMediaEngine::Terminate() {
|
||||
video_->Terminate();
|
||||
voice_->Terminate();
|
||||
}
|
||||
|
||||
int WebRtcMediaEngine::GetCapabilities() {
|
||||
return (voice_->GetCapabilities() | video_->GetCapabilities());
|
||||
}
|
||||
|
||||
cricket::VoiceMediaChannel* WebRtcMediaEngine::CreateChannel() {
|
||||
return voice_->CreateChannel();
|
||||
}
|
||||
|
||||
cricket::VideoMediaChannel* WebRtcMediaEngine::CreateVideoChannel(
|
||||
cricket::VoiceMediaChannel* channel) {
|
||||
return video_->CreateChannel(channel);
|
||||
}
|
||||
|
||||
cricket::SoundclipMedia* WebRtcMediaEngine::CreateSoundclip() {
|
||||
return voice_->CreateSoundclip();
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::SetAudioOptions(int o) {
|
||||
return voice_->SetOptions(o);
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::SetVideoOptions(int o) {
|
||||
return video_->SetOptions(o);
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::SetDefaultVideoEncoderConfig(
|
||||
const cricket::VideoEncoderConfig& config) {
|
||||
return video_->SetDefaultEncoderConfig(config);
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::SetSoundDevices(const cricket::Device* in_device,
|
||||
const cricket::Device* out_device) {
|
||||
return voice_->SetDevices(in_device, out_device);
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::SetVideoCaptureDevice(
|
||||
const cricket::Device* cam_device) {
|
||||
return video_->SetCaptureDevice(cam_device);
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::GetOutputVolume(int* level) {
|
||||
return voice_->GetOutputVolume(level);
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::SetOutputVolume(int level) {
|
||||
return voice_->SetOutputVolume(level);
|
||||
}
|
||||
|
||||
int WebRtcMediaEngine::GetInputLevel() {
|
||||
return voice_->GetInputLevel();
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::SetLocalMonitor(bool enable) {
|
||||
return voice_->SetLocalMonitor(enable);
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::SetLocalRenderer(cricket::VideoRenderer* renderer) {
|
||||
return video_->SetLocalRenderer(renderer);
|
||||
}
|
||||
|
||||
cricket::CaptureResult WebRtcMediaEngine::SetVideoCapture(bool capture) {
|
||||
return video_->SetCapture(capture);
|
||||
}
|
||||
|
||||
const std::vector<cricket::AudioCodec>& WebRtcMediaEngine::audio_codecs() {
|
||||
return voice_->codecs();
|
||||
}
|
||||
|
||||
const std::vector<cricket::VideoCodec>& WebRtcMediaEngine::video_codecs() {
|
||||
return video_->codecs();
|
||||
}
|
||||
|
||||
void WebRtcMediaEngine::SetVoiceLogging(int min_sev, const char* filter) {
|
||||
return voice_->SetLogging(min_sev, filter);
|
||||
}
|
||||
|
||||
void WebRtcMediaEngine::SetVideoLogging(int min_sev, const char* filter) {
|
||||
return video_->SetLogging(min_sev, filter);
|
||||
}
|
||||
|
||||
bool WebRtcMediaEngine::SetVideoCaptureModule(
|
||||
webrtc::VideoCaptureModule* vcm) {
|
||||
return video_->SetCaptureModule(vcm);
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 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_WEBRTCMEDIAENGINE_H_
|
||||
#define TALK_APP_WEBRTC_WEBRTCMEDIAENGINE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "talk/base/scoped_ptr.h"
|
||||
#include "talk/session/phone/mediaengine.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class WebRtcVideoEngine;
|
||||
class WebRtcVoiceEngine;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
class AudioDeviceModule;
|
||||
class VideoCaptureModule;
|
||||
}
|
||||
|
||||
// TODO(ronghuawu): chromium doesn't need to know the
|
||||
// detail about the cricket::MediaEngine interface.
|
||||
class WebRtcMediaEngine : public cricket::MediaEngine {
|
||||
public:
|
||||
WebRtcMediaEngine(webrtc::AudioDeviceModule* adm,
|
||||
webrtc::AudioDeviceModule* adm_sc, webrtc::VideoCaptureModule* vcm);
|
||||
virtual ~WebRtcMediaEngine();
|
||||
virtual bool Init();
|
||||
virtual void Terminate();
|
||||
virtual int GetCapabilities();
|
||||
virtual cricket::VoiceMediaChannel *CreateChannel();
|
||||
virtual cricket::VideoMediaChannel *CreateVideoChannel(
|
||||
cricket::VoiceMediaChannel* channel);
|
||||
virtual cricket::SoundclipMedia *CreateSoundclip();
|
||||
virtual bool SetAudioOptions(int o);
|
||||
virtual bool SetVideoOptions(int o);
|
||||
virtual bool SetDefaultVideoEncoderConfig(
|
||||
const cricket::VideoEncoderConfig& config);
|
||||
virtual bool SetSoundDevices(const cricket::Device* in_device,
|
||||
const cricket::Device* out_device);
|
||||
virtual bool SetVideoCaptureDevice(const cricket::Device* cam_device);
|
||||
virtual bool GetOutputVolume(int* level);
|
||||
virtual bool SetOutputVolume(int level);
|
||||
virtual int GetInputLevel();
|
||||
virtual bool SetLocalMonitor(bool enable);
|
||||
virtual bool SetLocalRenderer(cricket::VideoRenderer* renderer);
|
||||
virtual cricket::CaptureResult SetVideoCapture(bool capture);
|
||||
virtual const std::vector<cricket::AudioCodec>& audio_codecs();
|
||||
virtual const std::vector<cricket::VideoCodec>& video_codecs();
|
||||
virtual void SetVoiceLogging(int min_sev, const char* filter);
|
||||
virtual void SetVideoLogging(int min_sev, const char* filter);
|
||||
|
||||
// Allow the VCM be set later if not ready during the construction time
|
||||
bool SetVideoCaptureModule(webrtc::VideoCaptureModule* vcm);
|
||||
|
||||
protected:
|
||||
WebRtcMediaEngine();
|
||||
|
||||
talk_base::scoped_ptr<cricket::WebRtcVoiceEngine> voice_;
|
||||
talk_base::scoped_ptr<cricket::WebRtcVideoEngine> video_;
|
||||
};
|
||||
|
||||
#endif // TALK_APP_WEBRTC_WEBRTCMEDIAENGINE_H_
|
@ -1,600 +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/webrtc/webrtcsession.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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;
|
||||
|
||||
static const char kVideoStream[] = "video_rtp";
|
||||
static const char kAudioStream[] = "rtp";
|
||||
|
||||
WebRtcSession::WebRtcSession(
|
||||
const std::string& id,
|
||||
bool incoming,
|
||||
cricket::PortAllocator* allocator,
|
||||
cricket::ChannelManager* channelmgr,
|
||||
talk_base::Thread* signaling_thread)
|
||||
: BaseSession(signaling_thread),
|
||||
transport_(NULL),
|
||||
channel_manager_(channelmgr),
|
||||
transports_writable_(false),
|
||||
muted_(false),
|
||||
camera_muted_(false),
|
||||
setup_timeout_(kCallSetupTimeout),
|
||||
signaling_thread_(signaling_thread),
|
||||
id_(id),
|
||||
incoming_(incoming),
|
||||
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() {
|
||||
if (signaling_thread_ == NULL)
|
||||
return false;
|
||||
|
||||
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, true);
|
||||
ASSERT(voice_channel != NULL);
|
||||
stream_info->channel = voice_channel;
|
||||
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, true, NULL);
|
||||
ASSERT(video_channel != NULL);
|
||||
stream_info->channel = video_channel;
|
||||
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);
|
||||
return transport_channel;
|
||||
}
|
||||
|
||||
cricket::TransportChannel* WebRtcSession::GetChannel(
|
||||
const std::string& content_name, const std::string& name) {
|
||||
if (!transport_)
|
||||
return NULL;
|
||||
|
||||
return transport_->GetChannel(name);
|
||||
}
|
||||
|
||||
void WebRtcSession::DestroyChannel(
|
||||
const std::string& content_name, const std::string& name) {
|
||||
if (!transport_)
|
||||
return;
|
||||
|
||||
transport_->DestroyChannel(name);
|
||||
}
|
||||
|
||||
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<cricket::VideoChannel*>(
|
||||
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) {
|
||||
if (!sinfo->video) {
|
||||
cricket::VoiceChannel* channel = static_cast<cricket::VoiceChannel*> (
|
||||
sinfo->channel);
|
||||
channel->Enable(false);
|
||||
channel_manager_->DestroyVoiceChannel(channel);
|
||||
} else {
|
||||
cricket::VideoChannel* channel = static_cast<cricket::VideoChannel*> (
|
||||
sinfo->channel);
|
||||
channel->Enable(false);
|
||||
channel_manager_->DestroyVideoChannel(channel);
|
||||
}
|
||||
// channel and transport will be deleted in
|
||||
// DestroyVoiceChannel/DestroyVideoChannel
|
||||
streams_.erase(iter);
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
LOG(LERROR) << "No streams found for stream id " << stream_id;
|
||||
// TODO(ronghuawu): trigger onError callback
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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<std::string> 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<std::string>::iterator i = streams_to_remove.begin();
|
||||
i != streams_to_remove.end(); ++i) {
|
||||
RemoveStream(*i);
|
||||
}
|
||||
}
|
||||
|
||||
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 transports_writable = transport_->writable();
|
||||
if (transports_writable) {
|
||||
if (transports_writable != 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);
|
||||
}
|
||||
transports_writable_ = 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<cricket::Candidate>& candidates) {
|
||||
if (!offer) {
|
||||
LOG(LERROR) << "No SessionDescription from peer";
|
||||
return false;
|
||||
}
|
||||
|
||||
talk_base::scoped_ptr<cricket::SessionDescription> 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;
|
||||
}
|
||||
}
|
||||
// Provide remote candidates to the transport
|
||||
transport_->OnRemoteCandidates(candidates);
|
||||
|
||||
set_remote_description(offer);
|
||||
SetState(STATE_RECEIVEDINITIATE);
|
||||
|
||||
transport_->ConnectChannels();
|
||||
EnableAllStreams();
|
||||
|
||||
set_local_description(answer.release());
|
||||
SetState(STATE_SENTACCEPT);
|
||||
|
||||
// AddStream called only once with Video label
|
||||
if (video_content) {
|
||||
SignalAddStream(video_content->name, true);
|
||||
} else {
|
||||
SignalAddStream(audio_content->name, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcSession::OnRemoteDescription(
|
||||
cricket::SessionDescription* desc,
|
||||
const std::vector<cricket::Candidate>& candidates) {
|
||||
if (state() == STATE_SENTACCEPT ||
|
||||
state() == STATE_RECEIVEDACCEPT ||
|
||||
state() == STATE_INPROGRESS) {
|
||||
transport_->OnRemoteCandidates(candidates);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Session description is always accepted.
|
||||
set_remote_description(desc);
|
||||
SetState(STATE_RECEIVEDACCEPT);
|
||||
// Will trigger OnWritableState() if successful.
|
||||
transport_->OnRemoteCandidates(candidates);
|
||||
|
||||
if (!incoming()) {
|
||||
// Trigger OnAddStream callback at the initiator
|
||||
const cricket::ContentInfo* video_content = GetFirstVideoContent(desc);
|
||||
if (video_content && !SendSignalAddStream(true)) {
|
||||
LOG(LERROR) << "failed to find video stream in map";
|
||||
ASSERT(false);
|
||||
} else {
|
||||
const cricket::ContentInfo* audio_content = GetFirstAudioContent(desc);
|
||||
if (audio_content && !SendSignalAddStream(false)) {
|
||||
LOG(LERROR) << "failed to find audio stream in map";
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcSession::SendSignalAddStream(bool video) {
|
||||
StreamMap::const_iterator iter;
|
||||
for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
|
||||
StreamInfo* sinfo = (*iter);
|
||||
if (sinfo->video == video) {
|
||||
SignalAddStream(sinfo->stream_id, video);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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<cricket::VideoCodec> video_codecs;
|
||||
channel_manager_->GetSupportedVideoCodecs(&video_codecs);
|
||||
for (VideoCodecs::const_iterator codec = video_codecs.begin();
|
||||
codec != video_codecs.end(); ++codec) {
|
||||
video->AddCodec(*codec);
|
||||
}
|
||||
|
||||
// enabling RTCP mux by default at both ends, without
|
||||
// exchanging it through signaling message.
|
||||
video->set_rtcp_mux(true);
|
||||
video->SortCodecs();
|
||||
offer->AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP, video);
|
||||
} else {
|
||||
cricket::AudioContentDescription* audio =
|
||||
new cricket::AudioContentDescription();
|
||||
|
||||
std::vector<cricket::AudioCodec> audio_codecs;
|
||||
channel_manager_->GetSupportedAudioCodecs(&audio_codecs);
|
||||
for (AudioCodecs::const_iterator codec = audio_codecs.begin();
|
||||
codec != audio_codecs.end(); ++codec) {
|
||||
audio->AddCodec(*codec);
|
||||
}
|
||||
|
||||
// enabling RTCP mux by default at both ends, without
|
||||
// exchanging it through signaling message.
|
||||
audio->set_rtcp_mux(true);
|
||||
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<const cricket::AudioContentDescription*>(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RTCP mux is set based on what present in incoming offer
|
||||
audio_accept->set_rtcp_mux(audio_offer->rtcp_mux());
|
||||
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<const cricket::VideoContentDescription*>(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RTCP mux is set based on what present in incoming offer
|
||||
video_accept->set_rtcp_mux(video_offer->rtcp_mux());
|
||||
video_accept->SortCodecs();
|
||||
answer->AddContent(video_content->name, video_content->type, video_accept);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
void WebRtcSession::SetError(Error error) {
|
||||
BaseSession::SetError(error);
|
||||
}
|
||||
|
||||
void WebRtcSession::OnCandidatesReady(
|
||||
cricket::Transport* transport,
|
||||
const std::vector<cricket::Candidate>& candidates) {
|
||||
std::vector<cricket::Candidate>::const_iterator iter;
|
||||
for (iter = candidates.begin(); iter != candidates.end(); ++iter) {
|
||||
local_candidates_.push_back(*iter);
|
||||
}
|
||||
SignalLocalDescription(local_description(), candidates);
|
||||
}
|
||||
} /* namespace webrtc */
|
@ -1,222 +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 <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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"
|
||||
|
||||
namespace cricket {
|
||||
class ChannelManager;
|
||||
class Transport;
|
||||
class TransportChannel;
|
||||
class VoiceChannel;
|
||||
class VideoChannel;
|
||||
struct ConnectionInfo;
|
||||
}
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
typedef std::vector<cricket::AudioCodec> AudioCodecs;
|
||||
typedef std::vector<cricket::VideoCodec> VideoCodecs;
|
||||
|
||||
class WebRtcSession : public cricket::BaseSession {
|
||||
public:
|
||||
WebRtcSession(const std::string& id,
|
||||
bool incoming,
|
||||
cricket::PortAllocator* allocator,
|
||||
cricket::ChannelManager* channelmgr,
|
||||
talk_base::Thread* signaling_thread);
|
||||
|
||||
~WebRtcSession();
|
||||
|
||||
bool Initiate();
|
||||
bool Connect();
|
||||
bool OnRemoteDescription(cricket::SessionDescription* sdp,
|
||||
const std::vector<cricket::Candidate>& candidates);
|
||||
bool OnInitiateMessage(cricket::SessionDescription* sdp,
|
||||
const std::vector<cricket::Candidate>& candidates);
|
||||
bool CreateVoiceChannel(const std::string& stream_id);
|
||||
bool CreateVideoChannel(const std::string& stream_id);
|
||||
bool RemoveStream(const std::string& stream_id);
|
||||
void RemoveAllStreams();
|
||||
|
||||
// Returns true if we have either a voice or video stream matching this label.
|
||||
bool HasStream(const std::string& label) const;
|
||||
bool HasStream(bool video) const;
|
||||
|
||||
// Returns true if there's one or more audio channels in the session.
|
||||
bool HasAudioStream() const;
|
||||
|
||||
// Returns true if there's one or more video channels in the session.
|
||||
bool HasVideoStream() const;
|
||||
|
||||
bool SetVideoRenderer(const std::string& stream_id,
|
||||
cricket::VideoRenderer* renderer);
|
||||
|
||||
// This signal occurs when all the streams have been removed.
|
||||
// It is triggered by a successful call to the RemoveAllStream or
|
||||
// the OnRemoteDescription with stream deleted signaling message with the
|
||||
// candidates port equal to 0.
|
||||
sigslot::signal1<WebRtcSession*> SignalRemoveStreamMessage;
|
||||
|
||||
// This signal indicates a stream has been added properly.
|
||||
// It is triggered by a successful call to the OnInitiateMessage or
|
||||
// the OnRemoteDescription and if it's going to the STATE_RECEIVEDACCEPT.
|
||||
sigslot::signal2<const std::string&, bool> SignalAddStream;
|
||||
|
||||
// This signal occurs when one stream is removed with the signaling
|
||||
// message from the remote peer with the candidates port equal to 0.
|
||||
sigslot::signal2<const std::string&, bool> SignalRemoveStream;
|
||||
|
||||
// This signal occurs when the local candidate is ready
|
||||
sigslot::signal2<const cricket::SessionDescription*,
|
||||
const std::vector<cricket::Candidate>&> SignalLocalDescription;
|
||||
|
||||
// This signal triggers when setting up or resuming a call has not been
|
||||
// successful before a certain time out.
|
||||
sigslot::signal0<> SignalFailedCall;
|
||||
|
||||
bool muted() const { return muted_; }
|
||||
bool camera_muted() const { return camera_muted_; }
|
||||
const std::vector<cricket::Candidate>& local_candidates() {
|
||||
return local_candidates_;
|
||||
}
|
||||
const std::string& id() const { return id_; }
|
||||
void set_incoming(bool incoming) { incoming_ = incoming; }
|
||||
bool incoming() const { return incoming_; }
|
||||
cricket::PortAllocator* port_allocator() const { return port_allocator_; }
|
||||
talk_base::Thread* signaling_thread() const { return signaling_thread_; }
|
||||
|
||||
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(
|
||||
const std::string& content_name, const std::string& name);
|
||||
virtual void DestroyChannel(
|
||||
const std::string& content_name, const std::string& name);
|
||||
|
||||
private:
|
||||
struct StreamInfo {
|
||||
explicit StreamInfo(const std::string stream_id)
|
||||
: channel(NULL),
|
||||
video(false),
|
||||
stream_id(stream_id) {}
|
||||
|
||||
StreamInfo()
|
||||
: channel(NULL),
|
||||
video(false) {}
|
||||
cricket::BaseChannel* channel;
|
||||
bool video;
|
||||
std::string stream_id;
|
||||
};
|
||||
// Not really a map (vector).
|
||||
typedef std::vector<StreamInfo*> StreamMap;
|
||||
|
||||
// Dummy functions inherited from cricket::BaseSession.
|
||||
// They should never be called.
|
||||
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;
|
||||
}
|
||||
virtual talk_base::Thread* worker_thread();
|
||||
|
||||
// methods signaled by the transport
|
||||
void OnRequestSignaling(cricket::Transport* transport);
|
||||
void OnCandidatesReady(cricket::Transport* transport,
|
||||
const std::vector<cricket::Candidate>& candidates);
|
||||
void OnWritableState(cricket::Transport* transport);
|
||||
void OnTransportError(cricket::Transport* transport);
|
||||
void OnChannelGone(cricket::Transport* transport);
|
||||
|
||||
bool CheckForStreamDeleteMessage(
|
||||
const std::vector<cricket::Candidate>& candidates);
|
||||
void ProcessTerminateAccept(cricket::SessionDescription* desc);
|
||||
|
||||
void UpdateTransportWritableState();
|
||||
bool CheckAllTransportsWritable();
|
||||
void StartTransportTimeout(int timeout);
|
||||
void NotifyTransportState();
|
||||
|
||||
cricket::SessionDescription* CreateOffer();
|
||||
cricket::SessionDescription* CreateAnswer(
|
||||
const cricket::SessionDescription* answer);
|
||||
|
||||
// from MessageHandler
|
||||
virtual void OnMessage(talk_base::Message* message);
|
||||
|
||||
virtual cricket::Transport* CreateTransport();
|
||||
cricket::Transport* GetTransport();
|
||||
|
||||
typedef std::map<std::string, cricket::TransportChannel*> TransportChannelMap;
|
||||
|
||||
bool SetVideoCapture(bool capture);
|
||||
void EnableAllStreams();
|
||||
bool SendSignalAddStream(bool video);
|
||||
|
||||
cricket::Transport* transport_;
|
||||
cricket::ChannelManager* channel_manager_;
|
||||
std::vector<StreamInfo*> streams_;
|
||||
TransportChannelMap transport_channels_;
|
||||
bool transports_writable_;
|
||||
bool muted_;
|
||||
bool camera_muted_;
|
||||
int setup_timeout_;
|
||||
std::vector<cricket::Candidate> local_candidates_;
|
||||
|
||||
talk_base::Thread* signaling_thread_;
|
||||
std::string id_;
|
||||
bool incoming_;
|
||||
cricket::PortAllocator* port_allocator_;
|
||||
|
||||
static const char kIncomingDirection[];
|
||||
static const char kOutgoingDirection[];
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // TALK_APP_WEBRTC_WEBRTCSESSION_H_
|
@ -1,605 +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 <stdio.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "base/gunit.h"
|
||||
#include "base/helpers.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"
|
||||
|
||||
namespace {
|
||||
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<const cricket::MediaContentDescription*>(original);
|
||||
const cricket::ContentDescription* new_content_description = NULL;
|
||||
if (media->type() == cricket::MEDIA_TYPE_VIDEO) {
|
||||
const cricket::VideoContentDescription* video_description =
|
||||
static_cast<const cricket::VideoContentDescription*>(original);
|
||||
new_content_description = static_cast<const cricket::ContentDescription*>
|
||||
(CopyVideoContentDescription(video_description));
|
||||
} else if (media->type() == cricket::MEDIA_TYPE_AUDIO) {
|
||||
const cricket::AudioContentDescription* audio_description =
|
||||
static_cast<const cricket::AudioContentDescription*>(original);
|
||||
new_content_description = static_cast<const cricket::ContentDescription*>
|
||||
(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);
|
||||
new_content_infos.push_back(info);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
cricket::SessionDescription* GenerateFakeSessionDescription(bool video) {
|
||||
cricket::SessionDescription* fake_description = new cricket::SessionDescription();
|
||||
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<cricket::ContentDescription*>(video_dsc);
|
||||
} else {
|
||||
cricket::AudioContentDescription* audio_dsc =
|
||||
new cricket::AudioContentDescription();
|
||||
audio_dsc->SortCodecs();
|
||||
description = static_cast<cricket::ContentDescription*>(audio_dsc);
|
||||
}
|
||||
|
||||
// Cannot fail.
|
||||
fake_description->AddContent(name, cricket::NS_JINGLE_RTP, description);
|
||||
return fake_description;
|
||||
}
|
||||
|
||||
void GenerateFakeCandidate(std::vector<cricket::Candidate>* candidates,
|
||||
bool video) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
cricket::SessionDescription* GenerateFakeSession(
|
||||
std::vector<cricket::Candidate>* candidates,
|
||||
bool video) {
|
||||
cricket::SessionDescription* fake_description =
|
||||
GenerateFakeSessionDescription(video);
|
||||
if (fake_description == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
GenerateFakeCandidate(candidates, video);
|
||||
return fake_description;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class OnSignalImpl
|
||||
: public sigslot::has_slots<> {
|
||||
public:
|
||||
enum CallbackId {
|
||||
kNone,
|
||||
kOnAddStream,
|
||||
kOnRemoveStream,
|
||||
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 OnRemoveStream(const std::string& stream_id, bool video) {
|
||||
callback_ids_.push_back(kOnRemoveStream);
|
||||
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<cricket::Candidate>& 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<cricket::Candidate>* 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<CallbackId> callback_ids_;
|
||||
|
||||
std::string last_stream_id_;
|
||||
bool last_was_video_;
|
||||
cricket::SessionDescription* last_description_ptr_;
|
||||
std::vector<cricket::Candidate> last_candidates_;
|
||||
};
|
||||
|
||||
class WebRtcSessionTest : public OnSignalImpl {
|
||||
public:
|
||||
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;
|
||||
}
|
||||
|
||||
bool WaitForCallback(CallbackId id, int timeout_ms) {
|
||||
bool success = false;
|
||||
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;
|
||||
}
|
||||
talk_base::Thread::Current()->ProcessMessages(1);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Init(bool receiving) {
|
||||
if (signaling_thread_ != NULL)
|
||||
return false;
|
||||
signaling_thread_ = talk_base::Thread::Current();
|
||||
receiving_ = receiving;
|
||||
|
||||
if (worker_thread_!= NULL)
|
||||
return false;
|
||||
worker_thread_ = talk_base::Thread::Current();
|
||||
|
||||
cricket::FakePortAllocator* fake_port_allocator =
|
||||
new cricket::FakePortAllocator(worker_thread_, NULL);
|
||||
|
||||
allocator_ = static_cast<cricket::PortAllocator*>(fake_port_allocator);
|
||||
|
||||
channel_manager_ = new cricket::ChannelManager(worker_thread_);
|
||||
if (!channel_manager_->Init())
|
||||
return false;
|
||||
|
||||
talk_base::CreateRandomString(8, &id_);
|
||||
|
||||
session_ = new webrtc::WebRtcSession(
|
||||
id_, receiving_ , allocator_,
|
||||
channel_manager_,
|
||||
signaling_thread_);
|
||||
session_->SignalAddStream.connect(
|
||||
static_cast<OnSignalImpl*> (this),
|
||||
&OnSignalImpl::OnAddStream);
|
||||
session_->SignalRemoveStream.connect(
|
||||
static_cast<OnSignalImpl*> (this),
|
||||
&OnSignalImpl::OnRemoveStream);
|
||||
session_->SignalRtcMediaChannelCreated.connect(
|
||||
static_cast<OnSignalImpl*> (this),
|
||||
&OnSignalImpl::OnRtcMediaChannelCreated);
|
||||
session_->SignalLocalDescription.connect(
|
||||
static_cast<OnSignalImpl*> (this),
|
||||
&OnSignalImpl::OnLocalDescription);
|
||||
session_->SignalFailedCall.connect(
|
||||
static_cast<OnSignalImpl*> (this),
|
||||
&OnSignalImpl::OnFailedCall);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Terminate() {
|
||||
delete session_;
|
||||
session_ = NULL;
|
||||
delete channel_manager_;
|
||||
channel_manager_ = NULL;
|
||||
delete allocator_;
|
||||
allocator_ = NULL;
|
||||
}
|
||||
|
||||
~WebRtcSessionTest() {
|
||||
Terminate();
|
||||
}
|
||||
|
||||
// All session APIs must be called from the signaling thread.
|
||||
bool CallInitiate() {
|
||||
return session_->Initiate();
|
||||
}
|
||||
|
||||
bool CallConnect() {
|
||||
if (!session_->Connect())
|
||||
return false;
|
||||
// This callback does not happen with FakeTransport!
|
||||
if (!WaitForCallback(kOnLocalDescription, 1000)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CallOnRemoteDescription(
|
||||
cricket::SessionDescription* description,
|
||||
std::vector<cricket::Candidate> candidates) {
|
||||
if (!session_->OnRemoteDescription(description, candidates)) {
|
||||
return false;
|
||||
}
|
||||
if (!WaitForCallback(kOnAddStream, 1000)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CallOnInitiateMessage(
|
||||
cricket::SessionDescription* description,
|
||||
const std::vector<cricket::Candidate>& candidates) {
|
||||
if (!session_->OnInitiateMessage(description, candidates)) {
|
||||
return false;
|
||||
}
|
||||
if (!WaitForCallback(kOnAddStream, 1000)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CallCreateVoiceChannel(const std::string& stream_id) {
|
||||
if (!session_->CreateVoiceChannel(stream_id)) {
|
||||
return false;
|
||||
}
|
||||
if (!WaitForCallback(kOnRtcMediaChannelCreated, 1000)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CallCreateVideoChannel(const std::string& stream_id) {
|
||||
if (!session_->CreateVideoChannel(stream_id)) {
|
||||
return false;
|
||||
}
|
||||
if (!WaitForCallback(kOnRtcMediaChannelCreated, 1000)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CallRemoveStream(const std::string& stream_id) {
|
||||
return session_->RemoveStream(stream_id);
|
||||
}
|
||||
|
||||
void CallRemoveAllStreams() {
|
||||
session_->RemoveAllStreams();
|
||||
}
|
||||
|
||||
bool CallHasStream(const std::string& label) {
|
||||
return session_->HasStream(label);
|
||||
}
|
||||
|
||||
bool CallHasStream(bool video) {
|
||||
return session_->HasStream(video);
|
||||
}
|
||||
|
||||
bool CallHasAudioStream() {
|
||||
return session_->HasAudioStream();
|
||||
}
|
||||
|
||||
bool CallHasVideoStream() {
|
||||
return session_->HasVideoStream();
|
||||
}
|
||||
|
||||
bool CallSetVideoRenderer(const std::string& stream_id,
|
||||
cricket::VideoRenderer* renderer) {
|
||||
return session_->SetVideoRenderer(stream_id, renderer);
|
||||
}
|
||||
|
||||
const std::vector<cricket::Candidate>& CallLocalCandidates() {
|
||||
return session_->local_candidates();
|
||||
}
|
||||
|
||||
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) {
|
||||
talk_base::Thread::SleepMs(timeout);
|
||||
const OnSignalImpl::CallbackId peek_id =
|
||||
session->PeekOldestCallback();
|
||||
return peek_id != OnSignalImpl::kNone;
|
||||
}
|
||||
|
||||
void SleepMs(int timeout_ms) {
|
||||
talk_base::Thread::SleepMs(timeout_ms);
|
||||
}
|
||||
|
||||
TEST(WebRtcSessionTest, InitializationReceiveSanity) {
|
||||
const bool kReceiving = true;
|
||||
talk_base::scoped_ptr<WebRtcSessionTest> 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, AudioSendCallSetUp) {
|
||||
const bool kReceiving = false;
|
||||
talk_base::scoped_ptr<WebRtcSessionTest> 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());
|
||||
|
||||
std::vector<cricket::Candidate> candidates;
|
||||
cricket::SessionDescription* local_session = my_session->GetLocalDescription(
|
||||
&candidates);
|
||||
ASSERT_FALSE(candidates.empty());
|
||||
ASSERT_FALSE(local_session == NULL);
|
||||
if (!my_session->CallOnRemoteDescription(local_session, candidates)) {
|
||||
delete local_session;
|
||||
FAIL();
|
||||
}
|
||||
|
||||
// All callbacks should be caught by my_session. Assert it.
|
||||
ASSERT_FALSE(CallbackReceived(my_session.get(), 1000));
|
||||
ASSERT_TRUE(my_session->CallHasAudioStream() &&
|
||||
!my_session->CallHasVideoStream());
|
||||
|
||||
}
|
||||
|
||||
TEST(WebRtcSessionTest, VideoSendCallSetUp) {
|
||||
const bool kReceiving = false;
|
||||
talk_base::scoped_ptr<WebRtcSessionTest> my_session;
|
||||
my_session.reset(WebRtcSessionTest::CreateWebRtcSessionTest(kReceiving));
|
||||
|
||||
ASSERT_TRUE(my_session.get() != NULL);
|
||||
ASSERT_TRUE(my_session->CallInitiate());
|
||||
|
||||
ASSERT_TRUE(my_session->CallCreateVideoChannel("Video"));
|
||||
ASSERT_TRUE(my_session->CallConnect());
|
||||
|
||||
std::vector<cricket::Candidate> candidates;
|
||||
cricket::SessionDescription* local_session = my_session->GetLocalDescription(
|
||||
&candidates);
|
||||
ASSERT_FALSE(candidates.empty());
|
||||
ASSERT_FALSE(local_session == NULL);
|
||||
|
||||
if (!my_session->CallOnRemoteDescription(local_session, candidates)) {
|
||||
delete local_session;
|
||||
FAIL();
|
||||
}
|
||||
|
||||
// All callbacks should be caught by my_session. Assert it.
|
||||
ASSERT_FALSE(CallbackReceived(my_session.get(), 1000));
|
||||
ASSERT_TRUE(!my_session->CallHasAudioStream() &&
|
||||
my_session->CallHasVideoStream());
|
||||
}
|
||||
|
||||
TEST(WebRtcSessionTest, AudioReceiveCallSetUp) {
|
||||
const bool kReceiving = true;
|
||||
const bool video = false;
|
||||
|
||||
talk_base::scoped_ptr<WebRtcSessionTest> my_session;
|
||||
my_session.reset(WebRtcSessionTest::CreateWebRtcSessionTest(kReceiving));
|
||||
ASSERT_TRUE(my_session.get() != NULL);
|
||||
ASSERT_TRUE(my_session->CallInitiate());
|
||||
|
||||
std::vector<cricket::Candidate> candidates;
|
||||
cricket::SessionDescription* local_session =
|
||||
GenerateFakeSession(&candidates, video);
|
||||
ASSERT_FALSE(candidates.empty());
|
||||
ASSERT_FALSE(local_session == NULL);
|
||||
if (!my_session->CallOnInitiateMessage(local_session, candidates)) {
|
||||
delete local_session;
|
||||
FAIL();
|
||||
}
|
||||
ASSERT_TRUE(my_session->CallConnect());
|
||||
ASSERT_FALSE(CallbackReceived(my_session.get(), 1000));
|
||||
|
||||
ASSERT_TRUE(my_session->CallHasAudioStream() &&
|
||||
!my_session->CallHasVideoStream());
|
||||
}
|
||||
|
||||
TEST(WebRtcSessionTest, VideoReceiveCallSetUp) {
|
||||
const bool kReceiving = true;
|
||||
const bool video = true;
|
||||
|
||||
talk_base::scoped_ptr<WebRtcSessionTest> my_session;
|
||||
my_session.reset(WebRtcSessionTest::CreateWebRtcSessionTest(kReceiving));
|
||||
ASSERT_TRUE(my_session.get() != NULL);
|
||||
ASSERT_TRUE(my_session->CallInitiate());
|
||||
|
||||
std::vector<cricket::Candidate> candidates;
|
||||
cricket::SessionDescription* local_session =
|
||||
GenerateFakeSession(&candidates, video);
|
||||
ASSERT_FALSE(candidates.empty());
|
||||
ASSERT_FALSE(local_session == NULL);
|
||||
if (!my_session->CallOnInitiateMessage(local_session, candidates)) {
|
||||
delete local_session;
|
||||
FAIL();
|
||||
}
|
||||
ASSERT_TRUE(my_session->CallConnect());
|
||||
ASSERT_FALSE(CallbackReceived(my_session.get(), 1000));
|
||||
ASSERT_TRUE(!my_session->CallHasAudioStream() &&
|
||||
my_session->CallHasVideoStream());
|
||||
}
|
@ -1,217 +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/base/json.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
bool GetStringFromJson(const Json::Value& in, std::string* out) {
|
||||
if (!in.isString()) {
|
||||
std::ostringstream s;
|
||||
if (in.isBool()) {
|
||||
s << std::boolalpha << in.asBool();
|
||||
} else if (in.isInt()) {
|
||||
s << in.asInt();
|
||||
} else if (in.isUInt()) {
|
||||
s << in.asUInt();
|
||||
} else if (in.isDouble()) {
|
||||
s << in.asDouble();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
*out = s.str();
|
||||
} else {
|
||||
*out = in.asString();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetIntFromJson(const Json::Value& in, int* out) {
|
||||
bool ret;
|
||||
if (!in.isString()) {
|
||||
ret = in.isConvertibleTo(Json::intValue);
|
||||
if (ret) {
|
||||
*out = in.asInt();
|
||||
}
|
||||
} else {
|
||||
long val; // NOLINT
|
||||
const char* c_str = in.asCString();
|
||||
char* end_ptr;
|
||||
errno = 0;
|
||||
val = strtol(c_str, &end_ptr, 10); // NOLINT
|
||||
ret = (end_ptr != c_str && *end_ptr == '\0' && !errno &&
|
||||
val >= INT_MIN && val <= INT_MAX);
|
||||
*out = val;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool GetUIntFromJson(const Json::Value& in, unsigned int* out) {
|
||||
bool ret;
|
||||
if (!in.isString()) {
|
||||
ret = in.isConvertibleTo(Json::uintValue);
|
||||
if (ret) {
|
||||
*out = in.asUInt();
|
||||
}
|
||||
} else {
|
||||
unsigned long val; // NOLINT
|
||||
const char* c_str = in.asCString();
|
||||
char* end_ptr;
|
||||
errno = 0;
|
||||
val = strtoul(c_str, &end_ptr, 10); // NOLINT
|
||||
ret = (end_ptr != c_str && *end_ptr == '\0' && !errno &&
|
||||
val <= UINT_MAX);
|
||||
*out = val;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool GetBoolFromJson(const Json::Value& in, bool* out) {
|
||||
bool ret;
|
||||
if (!in.isString()) {
|
||||
ret = in.isConvertibleTo(Json::booleanValue);
|
||||
if (ret) {
|
||||
*out = in.asBool();
|
||||
}
|
||||
} else {
|
||||
if (in.asString() == "true") {
|
||||
*out = true;
|
||||
ret = true;
|
||||
} else if (in.asString() == "false") {
|
||||
*out = false;
|
||||
ret = true;
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool GetValueFromJsonArray(const Json::Value& in, size_t n,
|
||||
Json::Value* out) {
|
||||
if (!in.isArray() || !in.isValidIndex(n)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = in[static_cast<unsigned int>(n)];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetIntFromJsonArray(const Json::Value& in, size_t n,
|
||||
int* out) {
|
||||
Json::Value x;
|
||||
return GetValueFromJsonArray(in, n, &x) && GetIntFromJson(x, out);
|
||||
}
|
||||
|
||||
bool GetUIntFromJsonArray(const Json::Value& in, size_t n,
|
||||
unsigned int* out) {
|
||||
Json::Value x;
|
||||
return GetValueFromJsonArray(in, n, &x) && GetUIntFromJson(x, out);
|
||||
}
|
||||
|
||||
bool GetStringFromJsonArray(const Json::Value& in, size_t n,
|
||||
std::string* out) {
|
||||
Json::Value x;
|
||||
return GetValueFromJsonArray(in, n, &x) && GetStringFromJson(x, out);
|
||||
}
|
||||
|
||||
bool GetBoolFromJsonArray(const Json::Value& in, size_t n,
|
||||
bool* out) {
|
||||
Json::Value x;
|
||||
return GetValueFromJsonArray(in, n, &x) && GetBoolFromJson(x, out);
|
||||
}
|
||||
|
||||
bool GetValueFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
Json::Value* out) {
|
||||
if (!in.isObject() || !in.isMember(k)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = in[k];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool GetIntFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
int* out) {
|
||||
Json::Value x;
|
||||
return GetValueFromJsonObject(in, k, &x) && GetIntFromJson(x, out);
|
||||
}
|
||||
|
||||
bool GetUIntFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
unsigned int* out) {
|
||||
Json::Value x;
|
||||
return GetValueFromJsonObject(in, k, &x) && GetUIntFromJson(x, out);
|
||||
}
|
||||
|
||||
bool GetStringFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
std::string* out) {
|
||||
Json::Value x;
|
||||
return GetValueFromJsonObject(in, k, &x) && GetStringFromJson(x, out);
|
||||
}
|
||||
|
||||
bool GetBoolFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
bool* out) {
|
||||
Json::Value x;
|
||||
return GetValueFromJsonObject(in, k, &x) && GetBoolFromJson(x, out);
|
||||
}
|
||||
|
||||
Json::Value StringVectorToJsonValue(const std::vector<std::string>& strings) {
|
||||
Json::Value result(Json::arrayValue);
|
||||
for (size_t i = 0; i < strings.size(); ++i) {
|
||||
result.append(Json::Value(strings[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool JsonValueToStringVector(const Json::Value& value,
|
||||
std::vector<std::string> *strings) {
|
||||
strings->clear();
|
||||
if (!value.isArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < value.size(); ++i) {
|
||||
if (value[i].isString()) {
|
||||
strings->push_back(value[i].asString());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string JsonValueToString(const Json::Value& json) {
|
||||
Json::FastWriter w;
|
||||
std::string value = w.write(json);
|
||||
return value.substr(0, value.size() - 1); // trim trailing newline
|
||||
}
|
@ -1,80 +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_BASE_JSON_H_
|
||||
#define TALK_BASE_JSON_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "json/json.h"
|
||||
|
||||
// TODO(juberti): Move to talk_base namespace
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// JSON Helpers
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Robust conversion operators, better than the ones in JsonCpp.
|
||||
bool GetIntFromJson(const Json::Value& in, int* out);
|
||||
bool GetUIntFromJson(const Json::Value& in, unsigned int* out);
|
||||
bool GetStringFromJson(const Json::Value& in, std::string* out);
|
||||
bool GetBoolFromJson(const Json::Value& in, bool* out);
|
||||
|
||||
// Pull values out of a JSON array.
|
||||
bool GetValueFromJsonArray(const Json::Value& in, size_t n,
|
||||
Json::Value* out);
|
||||
bool GetIntFromJsonArray(const Json::Value& in, size_t n,
|
||||
int* out);
|
||||
bool GetUIntFromJsonArray(const Json::Value& in, size_t n,
|
||||
unsigned int* out);
|
||||
bool GetStringFromJsonArray(const Json::Value& in, size_t n,
|
||||
std::string* out);
|
||||
bool GetBoolFromJsonArray(const Json::Value& in, size_t n,
|
||||
bool* out);
|
||||
|
||||
// Pull values out of a JSON object.
|
||||
bool GetValueFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
Json::Value* out);
|
||||
bool GetIntFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
int* out);
|
||||
bool GetUIntFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
unsigned int* out);
|
||||
bool GetStringFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
std::string* out);
|
||||
bool GetBoolFromJsonObject(const Json::Value& in, const std::string& k,
|
||||
bool* out);
|
||||
|
||||
// Converts vectors of strings to/from JSON arrays.
|
||||
Json::Value StringVectorToJsonValue(const std::vector<std::string>& strings);
|
||||
bool JsonValueToStringVector(const Json::Value& value,
|
||||
std::vector<std::string> *strings);
|
||||
|
||||
// Writes out a Json value as a string.
|
||||
std::string JsonValueToString(const Json::Value& json);
|
||||
|
||||
#endif // TALK_BASE_JSON_H_
|
@ -1,799 +0,0 @@
|
||||
/*
|
||||
* libjingle
|
||||
* 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:
|
||||
*
|
||||
* 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/channelmanager.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "talk/base/common.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/sigslotrepeater.h"
|
||||
#include "talk/base/stringencode.h"
|
||||
#include "talk/session/phone/mediaengine.h"
|
||||
#include "talk/session/phone/soundclip.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
enum {
|
||||
MSG_CREATEVOICECHANNEL = 1,
|
||||
MSG_DESTROYVOICECHANNEL = 2,
|
||||
MSG_SETAUDIOOPTIONS = 3,
|
||||
MSG_GETOUTPUTVOLUME = 4,
|
||||
MSG_SETOUTPUTVOLUME = 5,
|
||||
MSG_SETLOCALMONITOR = 6,
|
||||
MSG_SETVOICELOGGING = 7,
|
||||
MSG_CREATEVIDEOCHANNEL = 11,
|
||||
MSG_DESTROYVIDEOCHANNEL = 12,
|
||||
MSG_SETVIDEOOPTIONS = 13,
|
||||
MSG_SETLOCALRENDERER = 14,
|
||||
MSG_SETDEFAULTVIDEOENCODERCONFIG = 15,
|
||||
MSG_SETVIDEOLOGGING = 16,
|
||||
MSG_CREATESOUNDCLIP = 17,
|
||||
MSG_DESTROYSOUNDCLIP = 18,
|
||||
MSG_CAMERASTARTED = 19,
|
||||
MSG_SETVIDEOCAPTURE = 20,
|
||||
};
|
||||
|
||||
struct CreationParams : public talk_base::MessageData {
|
||||
CreationParams(BaseSession* session, const std::string& content_name,
|
||||
bool rtcp, VoiceChannel* voice_channel)
|
||||
: session(session),
|
||||
content_name(content_name),
|
||||
rtcp(rtcp),
|
||||
voice_channel(voice_channel),
|
||||
video_channel(NULL) {}
|
||||
BaseSession* session;
|
||||
std::string content_name;
|
||||
bool rtcp;
|
||||
VoiceChannel* voice_channel;
|
||||
VideoChannel* video_channel;
|
||||
};
|
||||
|
||||
struct AudioOptions : public talk_base::MessageData {
|
||||
AudioOptions(int o, const Device* in, const Device* out)
|
||||
: options(o), in_device(in), out_device(out) {}
|
||||
int options;
|
||||
const Device* in_device;
|
||||
const Device* out_device;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct VolumeLevel : public talk_base::MessageData {
|
||||
VolumeLevel() : level(-1), result(false) {}
|
||||
explicit VolumeLevel(int l) : level(l), result(false) {}
|
||||
int level;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct VideoOptions : public talk_base::MessageData {
|
||||
explicit VideoOptions(const Device* d) : cam_device(d), result(false) {}
|
||||
const Device* cam_device;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct DefaultVideoEncoderConfig : public talk_base::MessageData {
|
||||
explicit DefaultVideoEncoderConfig(const VideoEncoderConfig& c)
|
||||
: config(c), result(false) {}
|
||||
VideoEncoderConfig config;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct LocalMonitor : public talk_base::MessageData {
|
||||
explicit LocalMonitor(bool e) : enable(e), result(false) {}
|
||||
bool enable;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct LocalRenderer : public talk_base::MessageData {
|
||||
explicit LocalRenderer(VideoRenderer* r) : renderer(r), result(false) {}
|
||||
VideoRenderer* renderer;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct LoggingOptions : public talk_base::MessageData {
|
||||
explicit LoggingOptions(int lev, const char* f) : level(lev), filter(f) {}
|
||||
int level;
|
||||
std::string filter;
|
||||
};
|
||||
|
||||
struct CaptureParams : public talk_base::MessageData {
|
||||
explicit CaptureParams(bool c) : capture(c), result(CR_FAILURE) {}
|
||||
|
||||
bool capture;
|
||||
CaptureResult result;
|
||||
};
|
||||
|
||||
ChannelManager::ChannelManager(talk_base::Thread* worker_thread)
|
||||
: media_engine_(MediaEngine::Create()),
|
||||
device_manager_(new DeviceManager()),
|
||||
initialized_(false),
|
||||
main_thread_(talk_base::Thread::Current()),
|
||||
worker_thread_(worker_thread),
|
||||
audio_in_device_(DeviceManager::kDefaultDeviceName),
|
||||
audio_out_device_(DeviceManager::kDefaultDeviceName),
|
||||
audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS),
|
||||
local_renderer_(NULL),
|
||||
capturing_(false),
|
||||
monitoring_(false) {
|
||||
Construct();
|
||||
}
|
||||
|
||||
ChannelManager::ChannelManager(MediaEngine* me, DeviceManager* dm,
|
||||
talk_base::Thread* worker_thread)
|
||||
: media_engine_(me),
|
||||
device_manager_(dm),
|
||||
initialized_(false),
|
||||
main_thread_(talk_base::Thread::Current()),
|
||||
worker_thread_(worker_thread),
|
||||
audio_in_device_(DeviceManager::kDefaultDeviceName),
|
||||
audio_out_device_(DeviceManager::kDefaultDeviceName),
|
||||
audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS),
|
||||
local_renderer_(NULL),
|
||||
capturing_(false),
|
||||
monitoring_(false) {
|
||||
Construct();
|
||||
}
|
||||
|
||||
void ChannelManager::Construct() {
|
||||
// Init the device manager immediately, and set up our default video device.
|
||||
SignalDevicesChange.repeat(device_manager_->SignalDevicesChange);
|
||||
device_manager_->Init();
|
||||
// Set camera_device_ to the name of the default video capturer.
|
||||
SetVideoOptions(DeviceManager::kDefaultDeviceName);
|
||||
|
||||
// Camera is started asynchronously, request callbacks when startup
|
||||
// completes to be able to forward them to the rendering manager.
|
||||
media_engine_->SignalVideoCaptureResult.connect(
|
||||
this, &ChannelManager::OnVideoCaptureResult);
|
||||
}
|
||||
|
||||
ChannelManager::~ChannelManager() {
|
||||
if (initialized_)
|
||||
Terminate();
|
||||
}
|
||||
|
||||
int ChannelManager::GetCapabilities() {
|
||||
return media_engine_->GetCapabilities() & device_manager_->GetCapabilities();
|
||||
}
|
||||
|
||||
void ChannelManager::GetSupportedAudioCodecs(
|
||||
std::vector<AudioCodec>* codecs) const {
|
||||
codecs->clear();
|
||||
|
||||
for (std::vector<AudioCodec>::const_iterator it =
|
||||
media_engine_->audio_codecs().begin();
|
||||
it != media_engine_->audio_codecs().end(); ++it) {
|
||||
codecs->push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelManager::GetSupportedVideoCodecs(
|
||||
std::vector<VideoCodec>* codecs) const {
|
||||
codecs->clear();
|
||||
|
||||
std::vector<VideoCodec>::const_iterator it;
|
||||
for (it = media_engine_->video_codecs().begin();
|
||||
it != media_engine_->video_codecs().end(); ++it) {
|
||||
codecs->push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChannelManager::Init() {
|
||||
ASSERT(!initialized_);
|
||||
if (initialized_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ASSERT(worker_thread_ != NULL);
|
||||
if (worker_thread_) {
|
||||
if (media_engine_->Init()) {
|
||||
initialized_ = true;
|
||||
|
||||
// Now that we're initialized, apply any stored preferences. A preferred
|
||||
// device might have been unplugged. In this case, we fallback to the
|
||||
// default device but keep the user preferences. The preferences are
|
||||
// changed only when the Javascript FE changes them.
|
||||
const std::string preferred_audio_in_device = audio_in_device_;
|
||||
const std::string preferred_audio_out_device = audio_out_device_;
|
||||
const std::string preferred_camera_device = camera_device_;
|
||||
Device device;
|
||||
if (!device_manager_->GetAudioInputDevice(audio_in_device_, &device)) {
|
||||
LOG(LS_WARNING) << "The preferred microphone '" << audio_in_device_
|
||||
<< "' is unavailable. Fall back to the default.";
|
||||
audio_in_device_ = DeviceManager::kDefaultDeviceName;
|
||||
}
|
||||
if (!device_manager_->GetAudioOutputDevice(audio_out_device_, &device)) {
|
||||
LOG(LS_WARNING) << "The preferred speaker '" << audio_out_device_
|
||||
<< "' is unavailable. Fall back to the default.";
|
||||
audio_out_device_ = DeviceManager::kDefaultDeviceName;
|
||||
}
|
||||
if (!device_manager_->GetVideoCaptureDevice(camera_device_, &device)) {
|
||||
if (!camera_device_.empty()) {
|
||||
LOG(LS_WARNING) << "The preferred camera '" << camera_device_
|
||||
<< "' is unavailable. Fall back to the default.";
|
||||
}
|
||||
camera_device_ = DeviceManager::kDefaultDeviceName;
|
||||
}
|
||||
|
||||
if (!SetAudioOptions(audio_in_device_, audio_out_device_,
|
||||
audio_options_)) {
|
||||
LOG(LS_WARNING) << "Failed to SetAudioOptions with"
|
||||
<< " microphone: " << audio_in_device_
|
||||
<< " speaker: " << audio_out_device_
|
||||
<< " options: " << audio_options_;
|
||||
}
|
||||
if (!SetVideoOptions(camera_device_) && !camera_device_.empty()) {
|
||||
LOG(LS_WARNING) << "Failed to SetVideoOptions with camera: "
|
||||
<< camera_device_;
|
||||
}
|
||||
|
||||
// Restore the user preferences.
|
||||
audio_in_device_ = preferred_audio_in_device;
|
||||
audio_out_device_ = preferred_audio_out_device;
|
||||
camera_device_ = preferred_camera_device;
|
||||
|
||||
// Now apply the default video codec that has been set earlier.
|
||||
if (default_video_encoder_config_.max_codec.id != 0) {
|
||||
SetDefaultVideoEncoderConfig(default_video_encoder_config_);
|
||||
}
|
||||
// And the local renderer.
|
||||
if (local_renderer_) {
|
||||
SetLocalRenderer(local_renderer_);
|
||||
}
|
||||
}
|
||||
}
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
void ChannelManager::Terminate() {
|
||||
ASSERT(initialized_);
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to destroy the voice/video channels
|
||||
while (!video_channels_.empty()) {
|
||||
DestroyVideoChannel_w(video_channels_.back());
|
||||
}
|
||||
while (!voice_channels_.empty()) {
|
||||
DestroyVoiceChannel_w(voice_channels_.back());
|
||||
}
|
||||
while (!soundclips_.empty()) {
|
||||
DestroySoundclip_w(soundclips_.back());
|
||||
}
|
||||
|
||||
media_engine_->Terminate();
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
VoiceChannel* ChannelManager::CreateVoiceChannel(
|
||||
BaseSession* session, const std::string& content_name, bool rtcp) {
|
||||
CreationParams params(session, content_name, rtcp, NULL);
|
||||
return (Send(MSG_CREATEVOICECHANNEL, ¶ms)) ? params.voice_channel : NULL;
|
||||
}
|
||||
|
||||
VoiceChannel* ChannelManager::CreateVoiceChannel_w(
|
||||
BaseSession* session, const std::string& content_name, bool rtcp) {
|
||||
talk_base::CritScope cs(&crit_);
|
||||
|
||||
// This is ok to alloc from a thread other than the worker thread
|
||||
ASSERT(initialized_);
|
||||
VoiceMediaChannel* media_channel = media_engine_->CreateChannel();
|
||||
if (media_channel == NULL)
|
||||
return NULL;
|
||||
|
||||
VoiceChannel* voice_channel = new VoiceChannel(
|
||||
worker_thread_, media_engine_.get(), media_channel,
|
||||
session, content_name, rtcp);
|
||||
voice_channels_.push_back(voice_channel);
|
||||
return voice_channel;
|
||||
}
|
||||
|
||||
void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) {
|
||||
if (voice_channel) {
|
||||
talk_base::TypedMessageData<VoiceChannel *> data(voice_channel);
|
||||
Send(MSG_DESTROYVOICECHANNEL, &data);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel) {
|
||||
talk_base::CritScope cs(&crit_);
|
||||
// Destroy voice channel.
|
||||
ASSERT(initialized_);
|
||||
VoiceChannels::iterator it = std::find(voice_channels_.begin(),
|
||||
voice_channels_.end(), voice_channel);
|
||||
ASSERT(it != voice_channels_.end());
|
||||
if (it == voice_channels_.end())
|
||||
return;
|
||||
|
||||
voice_channels_.erase(it);
|
||||
delete voice_channel;
|
||||
}
|
||||
|
||||
VideoChannel* ChannelManager::CreateVideoChannel(
|
||||
BaseSession* session, const std::string& content_name, bool rtcp,
|
||||
VoiceChannel* voice_channel) {
|
||||
CreationParams params(session, content_name, rtcp, voice_channel);
|
||||
return (Send(MSG_CREATEVIDEOCHANNEL, ¶ms)) ? params.video_channel : NULL;
|
||||
}
|
||||
|
||||
VideoChannel* ChannelManager::CreateVideoChannel_w(
|
||||
BaseSession* session, const std::string& content_name, bool rtcp,
|
||||
VoiceChannel* voice_channel) {
|
||||
talk_base::CritScope cs(&crit_);
|
||||
|
||||
// This is ok to alloc from a thread other than the worker thread
|
||||
ASSERT(initialized_);
|
||||
VideoMediaChannel* media_channel =
|
||||
// voice_channel can be NULL in case of NullVoiceEngine.
|
||||
media_engine_->CreateVideoChannel(voice_channel ?
|
||||
voice_channel->media_channel() : NULL);
|
||||
if (media_channel == NULL)
|
||||
return NULL;
|
||||
|
||||
VideoChannel* video_channel = new VideoChannel(
|
||||
worker_thread_, media_engine_.get(), media_channel,
|
||||
session, content_name, rtcp, voice_channel);
|
||||
video_channels_.push_back(video_channel);
|
||||
return video_channel;
|
||||
}
|
||||
|
||||
void ChannelManager::DestroyVideoChannel(VideoChannel* video_channel) {
|
||||
if (video_channel) {
|
||||
talk_base::TypedMessageData<VideoChannel *> data(video_channel);
|
||||
Send(MSG_DESTROYVIDEOCHANNEL, &data);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelManager::DestroyVideoChannel_w(VideoChannel *video_channel) {
|
||||
talk_base::CritScope cs(&crit_);
|
||||
// Destroy voice channel.
|
||||
ASSERT(initialized_);
|
||||
VideoChannels::iterator it = std::find(video_channels_.begin(),
|
||||
video_channels_.end(), video_channel);
|
||||
ASSERT(it != video_channels_.end());
|
||||
if (it == video_channels_.end())
|
||||
return;
|
||||
|
||||
video_channels_.erase(it);
|
||||
delete video_channel;
|
||||
}
|
||||
|
||||
Soundclip* ChannelManager::CreateSoundclip() {
|
||||
talk_base::TypedMessageData<Soundclip*> data(NULL);
|
||||
Send(MSG_CREATESOUNDCLIP, &data);
|
||||
return data.data();
|
||||
}
|
||||
|
||||
Soundclip* ChannelManager::CreateSoundclip_w() {
|
||||
talk_base::CritScope cs(&crit_);
|
||||
|
||||
ASSERT(initialized_);
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current());
|
||||
|
||||
SoundclipMedia* soundclip_media = media_engine_->CreateSoundclip();
|
||||
if (!soundclip_media) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Soundclip* soundclip = new Soundclip(worker_thread_, soundclip_media);
|
||||
soundclips_.push_back(soundclip);
|
||||
return soundclip;
|
||||
}
|
||||
|
||||
void ChannelManager::DestroySoundclip(Soundclip* soundclip) {
|
||||
if (soundclip) {
|
||||
talk_base::TypedMessageData<Soundclip*> data(soundclip);
|
||||
Send(MSG_DESTROYSOUNDCLIP, &data);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelManager::DestroySoundclip_w(Soundclip* soundclip) {
|
||||
talk_base::CritScope cs(&crit_);
|
||||
// Destroy soundclip.
|
||||
ASSERT(initialized_);
|
||||
Soundclips::iterator it = std::find(soundclips_.begin(),
|
||||
soundclips_.end(), soundclip);
|
||||
ASSERT(it != soundclips_.end());
|
||||
if (it == soundclips_.end())
|
||||
return;
|
||||
|
||||
soundclips_.erase(it);
|
||||
delete soundclip;
|
||||
}
|
||||
|
||||
bool ChannelManager::GetAudioOptions(std::string* in_name,
|
||||
std::string* out_name, int* opts) {
|
||||
*in_name = audio_in_device_;
|
||||
*out_name = audio_out_device_;
|
||||
*opts = audio_options_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChannelManager::SetAudioOptions(const std::string& in_name,
|
||||
const std::string& out_name, int opts) {
|
||||
// Get device ids from DeviceManager.
|
||||
Device in_dev, out_dev;
|
||||
if (!device_manager_->GetAudioInputDevice(in_name, &in_dev)) {
|
||||
LOG(LS_WARNING) << "Failed to GetAudioInputDevice: " << in_name;
|
||||
return false;
|
||||
}
|
||||
if (!device_manager_->GetAudioOutputDevice(out_name, &out_dev)) {
|
||||
LOG(LS_WARNING) << "Failed to GetAudioOutputDevice: " << out_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're initialized, pass the settings to the media engine.
|
||||
bool ret = true;
|
||||
if (initialized_) {
|
||||
AudioOptions options(opts, &in_dev, &out_dev);
|
||||
ret = (Send(MSG_SETAUDIOOPTIONS, &options) && options.result);
|
||||
}
|
||||
|
||||
// If all worked well, save the values for use in GetAudioOptions.
|
||||
if (ret) {
|
||||
audio_options_ = opts;
|
||||
audio_in_device_ = in_name;
|
||||
audio_out_device_ = out_name;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ChannelManager::SetAudioOptions_w(int opts, const Device* in_dev,
|
||||
const Device* out_dev) {
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current());
|
||||
ASSERT(initialized_);
|
||||
|
||||
// Set audio options
|
||||
bool ret = media_engine_->SetAudioOptions(opts);
|
||||
|
||||
// Set the audio devices
|
||||
if (ret) {
|
||||
talk_base::CritScope cs(&crit_);
|
||||
ret = media_engine_->SetSoundDevices(in_dev, out_dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ChannelManager::GetOutputVolume(int* level) {
|
||||
VolumeLevel volume;
|
||||
if (!Send(MSG_GETOUTPUTVOLUME, &volume) || !volume.result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*level = volume.level;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChannelManager::GetOutputVolume_w(int* level) {
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current());
|
||||
ASSERT(initialized_);
|
||||
return media_engine_->GetOutputVolume(level);
|
||||
}
|
||||
|
||||
bool ChannelManager::SetOutputVolume(int level) {
|
||||
VolumeLevel volume(level);
|
||||
return (Send(MSG_SETOUTPUTVOLUME, &volume) && volume.result);
|
||||
}
|
||||
|
||||
bool ChannelManager::SetOutputVolume_w(int level) {
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current());
|
||||
ASSERT(initialized_);
|
||||
return media_engine_->SetOutputVolume(level);
|
||||
}
|
||||
|
||||
bool ChannelManager::GetVideoOptions(std::string* cam_name) {
|
||||
*cam_name = camera_device_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChannelManager::SetVideoOptions(const std::string& cam_name) {
|
||||
Device device;
|
||||
if (!device_manager_->GetVideoCaptureDevice(cam_name, &device)) {
|
||||
if (!cam_name.empty()) {
|
||||
LOG(LS_WARNING) << "Device manager can't find camera: " << cam_name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're running, tell the media engine about it.
|
||||
bool ret = true;
|
||||
if (initialized_) {
|
||||
VideoOptions options(&device);
|
||||
ret = (Send(MSG_SETVIDEOOPTIONS, &options) && options.result);
|
||||
}
|
||||
|
||||
// If everything worked, retain the name of the selected camera.
|
||||
if (ret) {
|
||||
camera_device_ = device.name;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ChannelManager::SetVideoOptions_w(const Device* cam_device) {
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current());
|
||||
ASSERT(initialized_);
|
||||
|
||||
// Set the video input device
|
||||
return media_engine_->SetVideoCaptureDevice(cam_device);
|
||||
}
|
||||
|
||||
bool ChannelManager::SetDefaultVideoEncoderConfig(const VideoEncoderConfig& c) {
|
||||
bool ret = true;
|
||||
if (initialized_) {
|
||||
DefaultVideoEncoderConfig config(c);
|
||||
ret = Send(MSG_SETDEFAULTVIDEOENCODERCONFIG, &config) && config.result;
|
||||
}
|
||||
if (ret) {
|
||||
default_video_encoder_config_ = c;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ChannelManager::SetDefaultVideoEncoderConfig_w(
|
||||
const VideoEncoderConfig& c) {
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current());
|
||||
ASSERT(initialized_);
|
||||
return media_engine_->SetDefaultVideoEncoderConfig(c);
|
||||
}
|
||||
|
||||
bool ChannelManager::SetLocalMonitor(bool enable) {
|
||||
LocalMonitor monitor(enable);
|
||||
bool ret = Send(MSG_SETLOCALMONITOR, &monitor) && monitor.result;
|
||||
if (ret) {
|
||||
monitoring_ = enable;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ChannelManager::SetLocalMonitor_w(bool enable) {
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current());
|
||||
ASSERT(initialized_);
|
||||
return media_engine_->SetLocalMonitor(enable);
|
||||
}
|
||||
|
||||
bool ChannelManager::SetLocalRenderer(VideoRenderer* renderer) {
|
||||
bool ret = true;
|
||||
if (initialized_) {
|
||||
LocalRenderer local(renderer);
|
||||
ret = (Send(MSG_SETLOCALRENDERER, &local) && local.result);
|
||||
}
|
||||
if (ret) {
|
||||
local_renderer_ = renderer;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ChannelManager::SetLocalRenderer_w(VideoRenderer* renderer) {
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current());
|
||||
ASSERT(initialized_);
|
||||
return media_engine_->SetLocalRenderer(renderer);
|
||||
}
|
||||
|
||||
CaptureResult ChannelManager::SetVideoCapture(bool capture) {
|
||||
bool ret;
|
||||
CaptureParams capture_params(capture);
|
||||
ret = (Send(MSG_SETVIDEOCAPTURE, &capture_params) &&
|
||||
(capture_params.result != CR_FAILURE));
|
||||
if (ret) {
|
||||
capturing_ = capture;
|
||||
}
|
||||
return capture_params.result;
|
||||
}
|
||||
|
||||
CaptureResult ChannelManager::SetVideoCapture_w(bool capture) {
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current());
|
||||
ASSERT(initialized_);
|
||||
return media_engine_->SetVideoCapture(capture);
|
||||
}
|
||||
|
||||
void ChannelManager::SetVoiceLogging(int level, const char* filter) {
|
||||
SetMediaLogging(false, level, filter);
|
||||
}
|
||||
|
||||
void ChannelManager::SetVideoLogging(int level, const char* filter) {
|
||||
SetMediaLogging(true, level, filter);
|
||||
}
|
||||
|
||||
void ChannelManager::SetMediaLogging(bool video, int level,
|
||||
const char* filter) {
|
||||
// Can be called before initialization; in this case, the worker function
|
||||
// is simply called on the main thread.
|
||||
if (initialized_) {
|
||||
LoggingOptions options(level, filter);
|
||||
Send((video) ? MSG_SETVIDEOLOGGING : MSG_SETVOICELOGGING, &options);
|
||||
} else {
|
||||
SetMediaLogging_w(video, level, filter);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelManager::SetMediaLogging_w(bool video, int level,
|
||||
const char* filter) {
|
||||
// Can be called before initialization
|
||||
ASSERT(worker_thread_ == talk_base::Thread::Current() || !initialized_);
|
||||
if (video) {
|
||||
media_engine_->SetVideoLogging(level, filter);
|
||||
} else {
|
||||
media_engine_->SetVoiceLogging(level, filter);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChannelManager::Send(uint32 id, talk_base::MessageData* data) {
|
||||
if (!worker_thread_ || !initialized_) return false;
|
||||
worker_thread_->Send(this, id, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ChannelManager::OnVideoCaptureResult(CaptureResult result) {
|
||||
capturing_ = result == CR_SUCCESS;
|
||||
main_thread_->Post(this, MSG_CAMERASTARTED,
|
||||
new talk_base::TypedMessageData<CaptureResult>(result));
|
||||
}
|
||||
|
||||
void ChannelManager::OnMessage(talk_base::Message* message) {
|
||||
talk_base::MessageData* data = message->pdata;
|
||||
switch (message->message_id) {
|
||||
case MSG_CREATEVOICECHANNEL: {
|
||||
CreationParams* p = static_cast<CreationParams*>(data);
|
||||
p->voice_channel =
|
||||
CreateVoiceChannel_w(p->session, p->content_name, p->rtcp);
|
||||
break;
|
||||
}
|
||||
case MSG_DESTROYVOICECHANNEL: {
|
||||
VoiceChannel* p = static_cast<talk_base::TypedMessageData<VoiceChannel*>*>
|
||||
(data)->data();
|
||||
DestroyVoiceChannel_w(p);
|
||||
break;
|
||||
}
|
||||
case MSG_CREATEVIDEOCHANNEL: {
|
||||
CreationParams* p = static_cast<CreationParams*>(data);
|
||||
p->video_channel = CreateVideoChannel_w(p->session, p->content_name,
|
||||
p->rtcp, p->voice_channel);
|
||||
break;
|
||||
}
|
||||
case MSG_DESTROYVIDEOCHANNEL: {
|
||||
VideoChannel* p = static_cast<talk_base::TypedMessageData<VideoChannel*>*>
|
||||
(data)->data();
|
||||
DestroyVideoChannel_w(p);
|
||||
break;
|
||||
}
|
||||
case MSG_CREATESOUNDCLIP: {
|
||||
talk_base::TypedMessageData<Soundclip*> *p =
|
||||
static_cast<talk_base::TypedMessageData<Soundclip*>*>(data);
|
||||
p->data() = CreateSoundclip_w();
|
||||
break;
|
||||
}
|
||||
case MSG_DESTROYSOUNDCLIP: {
|
||||
talk_base::TypedMessageData<Soundclip*> *p =
|
||||
static_cast<talk_base::TypedMessageData<Soundclip*>*>(data);
|
||||
DestroySoundclip_w(p->data());
|
||||
break;
|
||||
}
|
||||
case MSG_SETAUDIOOPTIONS: {
|
||||
AudioOptions* p = static_cast<AudioOptions*>(data);
|
||||
p->result = SetAudioOptions_w(p->options,
|
||||
p->in_device, p->out_device);
|
||||
break;
|
||||
}
|
||||
case MSG_GETOUTPUTVOLUME: {
|
||||
VolumeLevel* p = static_cast<VolumeLevel*>(data);
|
||||
p->result = GetOutputVolume_w(&p->level);
|
||||
break;
|
||||
}
|
||||
case MSG_SETOUTPUTVOLUME: {
|
||||
VolumeLevel* p = static_cast<VolumeLevel*>(data);
|
||||
p->result = SetOutputVolume_w(p->level);
|
||||
break;
|
||||
}
|
||||
case MSG_SETLOCALMONITOR: {
|
||||
LocalMonitor* p = static_cast<LocalMonitor*>(data);
|
||||
p->result = SetLocalMonitor_w(p->enable);
|
||||
break;
|
||||
}
|
||||
case MSG_SETVIDEOOPTIONS: {
|
||||
VideoOptions* p = static_cast<VideoOptions*>(data);
|
||||
p->result = SetVideoOptions_w(p->cam_device);
|
||||
break;
|
||||
}
|
||||
case MSG_SETDEFAULTVIDEOENCODERCONFIG: {
|
||||
DefaultVideoEncoderConfig* p =
|
||||
static_cast<DefaultVideoEncoderConfig*>(data);
|
||||
p->result = SetDefaultVideoEncoderConfig_w(p->config);
|
||||
break;
|
||||
}
|
||||
case MSG_SETLOCALRENDERER: {
|
||||
LocalRenderer* p = static_cast<LocalRenderer*>(data);
|
||||
p->result = SetLocalRenderer_w(p->renderer);
|
||||
break;
|
||||
}
|
||||
case MSG_SETVIDEOCAPTURE: {
|
||||
CaptureParams* p = static_cast<CaptureParams*>(data);
|
||||
p->result = SetVideoCapture_w(p->capture);
|
||||
break;
|
||||
}
|
||||
case MSG_SETVOICELOGGING:
|
||||
case MSG_SETVIDEOLOGGING: {
|
||||
LoggingOptions* p = static_cast<LoggingOptions*>(data);
|
||||
bool video = (message->message_id == MSG_SETVIDEOLOGGING);
|
||||
SetMediaLogging_w(video, p->level, p->filter.c_str());
|
||||
break;
|
||||
}
|
||||
case MSG_CAMERASTARTED: {
|
||||
talk_base::TypedMessageData<CaptureResult>* data =
|
||||
static_cast<talk_base::TypedMessageData<CaptureResult>*>(
|
||||
message->pdata);
|
||||
SignalVideoCaptureResult(data->data());
|
||||
delete data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void GetDeviceNames(const std::vector<Device>& devs,
|
||||
std::vector<std::string>* names) {
|
||||
names->clear();
|
||||
for (size_t i = 0; i < devs.size(); ++i) {
|
||||
names->push_back(devs[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChannelManager::GetAudioInputDevices(std::vector<std::string>* names) {
|
||||
names->clear();
|
||||
std::vector<Device> devs;
|
||||
bool ret = device_manager_->GetAudioInputDevices(&devs);
|
||||
if (ret)
|
||||
GetDeviceNames(devs, names);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ChannelManager::GetAudioOutputDevices(std::vector<std::string>* names) {
|
||||
names->clear();
|
||||
std::vector<Device> devs;
|
||||
bool ret = device_manager_->GetAudioOutputDevices(&devs);
|
||||
if (ret)
|
||||
GetDeviceNames(devs, names);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ChannelManager::GetVideoCaptureDevices(std::vector<std::string>* names) {
|
||||
names->clear();
|
||||
std::vector<Device> devs;
|
||||
bool ret = device_manager_->GetVideoCaptureDevices(&devs);
|
||||
if (ret)
|
||||
GetDeviceNames(devs, names);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
@ -35,7 +35,7 @@
|
||||
#include <ksmedia.h>
|
||||
#define INITGUID // For PKEY_AudioEndpoint_GUID
|
||||
#include <mmdeviceapi.h>
|
||||
#include <MMSystem.h>
|
||||
#include <mmsystem.h>
|
||||
#include <functiondiscoverykeys_devpkey.h>
|
||||
#include <uuids.h>
|
||||
#include "talk/base/win32.h" // ToUtf8
|
||||
@ -53,21 +53,24 @@
|
||||
#include "talk/base/stream.h"
|
||||
#include "talk/session/phone/libudevsymboltable.h"
|
||||
#include "talk/session/phone/v4llookup.h"
|
||||
#if defined(LINUX_SOUND_USED)
|
||||
#include "talk/sound/platformsoundsystem.h"
|
||||
#include "talk/sound/platformsoundsystemfactory.h"
|
||||
#include "talk/sound/sounddevicelocator.h"
|
||||
#include "talk/sound/soundsysteminterface.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/stringutils.h"
|
||||
#include "talk/session/phone/mediaengine.h"
|
||||
#include "talk/base/thread.h"
|
||||
#include "talk/session/phone/mediacommon.h"
|
||||
|
||||
namespace cricket {
|
||||
// Initialize to empty string.
|
||||
const std::string DeviceManager::kDefaultDeviceName;
|
||||
const char DeviceManagerInterface::kDefaultDeviceName[] = "";
|
||||
|
||||
DeviceManagerInterface* DeviceManagerFactory::Create() {
|
||||
return new DeviceManager();
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
class DeviceWatcher : public talk_base::Win32Window {
|
||||
@ -151,7 +154,7 @@ DeviceManager::DeviceManager()
|
||||
need_couninitialize_(false),
|
||||
#endif
|
||||
watcher_(new DeviceWatcher(this))
|
||||
#ifdef LINUX_SOUND_USED
|
||||
#ifdef LINUX
|
||||
, sound_system_(new PlatformSoundSystemFactory())
|
||||
#endif
|
||||
{
|
||||
@ -199,15 +202,15 @@ void DeviceManager::Terminate() {
|
||||
|
||||
int DeviceManager::GetCapabilities() {
|
||||
std::vector<Device> devices;
|
||||
int caps = MediaEngine::VIDEO_RECV;
|
||||
int caps = VIDEO_RECV;
|
||||
if (GetAudioInputDevices(&devices) && !devices.empty()) {
|
||||
caps |= MediaEngine::AUDIO_SEND;
|
||||
caps |= AUDIO_SEND;
|
||||
}
|
||||
if (GetAudioOutputDevices(&devices) && !devices.empty()) {
|
||||
caps |= MediaEngine::AUDIO_RECV;
|
||||
caps |= AUDIO_RECV;
|
||||
}
|
||||
if (GetVideoCaptureDevices(&devices) && !devices.empty()) {
|
||||
caps |= MediaEngine::VIDEO_SEND;
|
||||
caps |= VIDEO_SEND;
|
||||
}
|
||||
return caps;
|
||||
}
|
||||
@ -327,7 +330,8 @@ bool DeviceManager::GetAudioDevice(bool is_input, const std::string& name,
|
||||
bool DeviceManager::GetAudioDevicesByPlatform(bool input,
|
||||
std::vector<Device>* devs) {
|
||||
devs->clear();
|
||||
#if defined(LINUX_SOUND_USED)
|
||||
|
||||
#if defined(LINUX)
|
||||
if (!sound_system_.get()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,109 +0,0 @@
|
||||
/*
|
||||
* libjingle
|
||||
* 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:
|
||||
*
|
||||
* 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_DEVICEMANAGER_H_
|
||||
#define TALK_SESSION_PHONE_DEVICEMANAGER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "talk/base/sigslot.h"
|
||||
#include "talk/base/stringencode.h"
|
||||
#ifdef LINUX_SOUND_USED
|
||||
#include "talk/sound/soundsystemfactory.h"
|
||||
#endif
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class DeviceWatcher;
|
||||
|
||||
// Used to represent an audio or video capture or render device.
|
||||
struct Device {
|
||||
Device() {}
|
||||
Device(const std::string& first, int second)
|
||||
: name(first),
|
||||
id(talk_base::ToString(second)) {
|
||||
}
|
||||
Device(const std::string& first, const std::string& second)
|
||||
: name(first), id(second) {}
|
||||
|
||||
std::string name;
|
||||
std::string id;
|
||||
};
|
||||
|
||||
// DeviceManager manages the audio and video devices on the system.
|
||||
// Methods are virtual to allow for easy stubbing/mocking in tests.
|
||||
class DeviceManager {
|
||||
public:
|
||||
DeviceManager();
|
||||
virtual ~DeviceManager();
|
||||
|
||||
// Initialization
|
||||
virtual bool Init();
|
||||
virtual void Terminate();
|
||||
bool initialized() const { return initialized_; }
|
||||
|
||||
// Capabilities
|
||||
virtual int GetCapabilities();
|
||||
|
||||
// Device enumeration
|
||||
virtual bool GetAudioInputDevices(std::vector<Device>* devices);
|
||||
virtual bool GetAudioOutputDevices(std::vector<Device>* devices);
|
||||
|
||||
bool GetAudioInputDevice(const std::string& name, Device* out);
|
||||
bool GetAudioOutputDevice(const std::string& name, Device* out);
|
||||
|
||||
virtual bool GetVideoCaptureDevices(std::vector<Device>* devs);
|
||||
bool GetVideoCaptureDevice(const std::string& name, Device* out);
|
||||
|
||||
sigslot::signal0<> SignalDevicesChange;
|
||||
|
||||
void OnDevicesChange() { SignalDevicesChange(); }
|
||||
|
||||
static const std::string kDefaultDeviceName;
|
||||
|
||||
protected:
|
||||
virtual bool GetAudioDevice(bool is_input, const std::string& name,
|
||||
Device* out);
|
||||
virtual bool GetDefaultVideoCaptureDevice(Device* device);
|
||||
|
||||
private:
|
||||
bool GetAudioDevicesByPlatform(bool input, std::vector<Device>* devs);
|
||||
|
||||
bool initialized_;
|
||||
#ifdef WIN32
|
||||
bool need_couninitialize_;
|
||||
#endif
|
||||
DeviceWatcher* watcher_;
|
||||
#ifdef LINUX_SOUND_USED
|
||||
SoundSystemHandle sound_system_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // TALK_SESSION_PHONE_DEVICEMANAGER_H_
|
@ -1,660 +0,0 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#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<CryptoParams>& 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<CryptoParams>& 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<CryptoParams>& params,
|
||||
ContentSource source) {
|
||||
offer_params_ = params;
|
||||
state_ = (source == CS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SrtpFilter::NegotiateParams(const std::vector<CryptoParams>& 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<CryptoParams>::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<int>(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*> 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<ssrc_type_t>(type);
|
||||
policy.ssrc.value = 0;
|
||||
policy.key = const_cast<uint8*>(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<SrtpSession*>::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<int>(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
|
@ -1,84 +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_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_
|
@ -25,7 +25,7 @@
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_WEBRTC
|
||||
#ifdef HAVE_WEBRTC_VIDEO
|
||||
|
||||
#include "talk/session/phone/webrtcvideoengine.h"
|
||||
|
||||
@ -34,6 +34,8 @@
|
||||
#include "talk/base/byteorder.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/stringutils.h"
|
||||
#include "talk/session/phone/videorenderer.h"
|
||||
#include "talk/session/phone/webrtcpassthroughrender.h"
|
||||
#include "talk/session/phone/webrtcvoiceengine.h"
|
||||
#include "talk/session/phone/webrtcvideoframe.h"
|
||||
#include "talk/session/phone/webrtcvie.h"
|
||||
@ -53,9 +55,8 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer {
|
||||
|
||||
virtual int FrameSizeChange(unsigned int width, unsigned int height,
|
||||
unsigned int /*number_of_streams*/) {
|
||||
if (renderer_ == NULL) {
|
||||
if (renderer_ == NULL)
|
||||
return 0;
|
||||
}
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
return renderer_->SetSize(width_, height_, 0) ? 0 : -1;
|
||||
@ -63,14 +64,9 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer {
|
||||
|
||||
virtual int DeliverFrame(unsigned char* buffer, int buffer_size,
|
||||
unsigned int time_stamp) {
|
||||
if (renderer_ == NULL) {
|
||||
if (renderer_ == NULL)
|
||||
return 0;
|
||||
}
|
||||
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, time_stamp);
|
||||
int ret = renderer_->RenderFrame(&video_frame) ? 0 : -1;
|
||||
uint8* buffer_temp;
|
||||
@ -89,48 +85,72 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer {
|
||||
|
||||
const WebRtcVideoEngine::VideoCodecPref
|
||||
WebRtcVideoEngine::kVideoCodecPrefs[] = {
|
||||
{"VP8", 104, 0},
|
||||
{"H264", 105, 1}
|
||||
{"VP8", 120, 0},
|
||||
};
|
||||
|
||||
// The formats are sorted by the descending order of width. We use the order to
|
||||
// find the next format for CPU and bandwidth adaptation.
|
||||
const VideoFormat WebRtcVideoEngine::kVideoFormats[] = {
|
||||
// TODO: Understand why we have problem with 16:9 formats.
|
||||
VideoFormat(1280, 800, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
//VideoFormat(1280, 720, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
VideoFormat(960, 600, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
//VideoFormat(960, 540, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
VideoFormat(640, 400, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
//VideoFormat(640, 360, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
VideoFormat(480, 300, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
//VideoFormat(480, 270, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
VideoFormat(320, 200, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
//VideoFormat(320, 180, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
VideoFormat(240, 150, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
//VideoFormat(240, 135, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
VideoFormat(160, 100, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
//VideoFormat(160, 90, VideoFormat::FpsToInterval(30), FOURCC_ANY),
|
||||
};
|
||||
|
||||
// TODO: Understand why 640x400 is not working.
|
||||
const VideoFormat WebRtcVideoEngine::kDefaultVideoFormat =
|
||||
VideoFormat(320, 200, VideoFormat::FpsToInterval(30), FOURCC_ANY);
|
||||
|
||||
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) {
|
||||
if(capture_)
|
||||
capture_->AddRef();
|
||||
voice_engine_(NULL) {
|
||||
Construct();
|
||||
}
|
||||
|
||||
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) {
|
||||
voice_engine_(voice_engine) {
|
||||
Construct();
|
||||
}
|
||||
|
||||
void WebRtcVideoEngine::Construct() {
|
||||
initialized_ = false;
|
||||
capture_id_ = -1;
|
||||
capture_module_ = NULL;
|
||||
external_capture_ = false;
|
||||
log_level_ = kDefaultLogSeverity;
|
||||
capture_started_ = false;
|
||||
render_module_.reset(new WebRtcPassthroughRender());
|
||||
|
||||
ApplyLogging();
|
||||
if (vie_wrapper_->engine()->SetTraceCallback(this) != 0) {
|
||||
LOG_RTCERR1(SetTraceCallback, this);
|
||||
}
|
||||
|
||||
// Set default quality levels for our supported codecs. We override them here
|
||||
// if we know your cpu performance is low, and they can be updated explicitly
|
||||
// by calling SetDefaultCodec. For example by a flute preference setting, or
|
||||
// by the server with a jec in response to our reported system info.
|
||||
VideoCodec max_codec(kVideoCodecPrefs[0].payload_type,
|
||||
kVideoCodecPrefs[0].name,
|
||||
kDefaultVideoFormat.width,
|
||||
kDefaultVideoFormat.height,
|
||||
kDefaultVideoFormat.framerate(), 0);
|
||||
if (!SetDefaultCodec(max_codec)) {
|
||||
LOG(LS_ERROR) << "Failed to initialize list of supported codec types";
|
||||
}
|
||||
}
|
||||
|
||||
WebRtcVideoEngine::~WebRtcVideoEngine() {
|
||||
@ -138,21 +158,13 @@ WebRtcVideoEngine::~WebRtcVideoEngine() {
|
||||
vie_wrapper_->engine()->SetTraceCallback(NULL);
|
||||
Terminate();
|
||||
vie_wrapper_.reset();
|
||||
if (capture_) {
|
||||
capture_->Release();
|
||||
}
|
||||
if (renderer_) {
|
||||
webrtc::VideoRender::DestroyVideoRender(renderer_);
|
||||
if (capture_module_) {
|
||||
capture_module_->Release();
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
@ -184,28 +196,16 @@ bool WebRtcVideoEngine::InitVideoEngine() {
|
||||
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) {
|
||||
if (vie_wrapper_->render()->RegisterVideoRenderModule(
|
||||
*render_module_.get()) != 0) {
|
||||
LOG_RTCERR0(RegisterVideoRenderModule);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::sort(video_codecs_.begin(), video_codecs_.end(),
|
||||
&VideoCodec::Preferable);
|
||||
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -253,15 +253,6 @@ void WebRtcVideoEngine::Print(const webrtc::TraceLevel level,
|
||||
}
|
||||
}
|
||||
|
||||
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_) {
|
||||
@ -273,8 +264,33 @@ void WebRtcVideoEngine::ApplyLogging() {
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuilds the codec list to be only those that are less intensive
|
||||
// than the specified codec.
|
||||
bool WebRtcVideoEngine::RebuildCodecList(const VideoCodec& in_codec) {
|
||||
if (!FindCodec(in_codec))
|
||||
return false;
|
||||
|
||||
video_codecs_.clear();
|
||||
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(kVideoCodecPrefs); ++i) {
|
||||
const VideoCodecPref& pref(kVideoCodecPrefs[i]);
|
||||
if (!found)
|
||||
found = (in_codec.name == pref.name);
|
||||
if (found) {
|
||||
VideoCodec codec(pref.payload_type, pref.name,
|
||||
in_codec.width, in_codec.height, in_codec.framerate,
|
||||
ARRAY_SIZE(kVideoCodecPrefs) - i);
|
||||
video_codecs_.push_back(codec);
|
||||
}
|
||||
}
|
||||
ASSERT(found);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcVideoEngine::Terminate() {
|
||||
LOG(LS_INFO) << "WebRtcVideoEngine::Terminate";
|
||||
initialized_ = false;
|
||||
SetCapture(false);
|
||||
if (local_renderer_.get()) {
|
||||
// If the renderer already set, stop it first
|
||||
@ -282,7 +298,8 @@ void WebRtcVideoEngine::Terminate() {
|
||||
LOG_RTCERR1(StopRender, capture_id_);
|
||||
}
|
||||
|
||||
if (vie_wrapper_->render()->DeRegisterVideoRenderModule(*renderer_) != 0)
|
||||
if (vie_wrapper_->render()->DeRegisterVideoRenderModule(
|
||||
*render_module_.get()) != 0)
|
||||
LOG_RTCERR0(DeRegisterVideoRenderModule);
|
||||
|
||||
if ((vie_wrapper_->base()->DeregisterObserver()) != 0)
|
||||
@ -296,7 +313,7 @@ void WebRtcVideoEngine::Terminate() {
|
||||
}
|
||||
|
||||
int WebRtcVideoEngine::GetCapabilities() {
|
||||
return MediaEngine::VIDEO_RECV | MediaEngine::VIDEO_SEND;
|
||||
return VIDEO_RECV | VIDEO_SEND;
|
||||
}
|
||||
|
||||
bool WebRtcVideoEngine::SetOptions(int options) {
|
||||
@ -313,8 +330,11 @@ bool WebRtcVideoEngine::ReleaseCaptureDevice() {
|
||||
it != channels_.end(); ++it) {
|
||||
ASSERT(*it != NULL);
|
||||
channel = *it;
|
||||
// Ignore the return value here as the channel may not have connected to
|
||||
// the capturer yet.
|
||||
vie_wrapper_->capture()->DisconnectCaptureDevice(
|
||||
channel->video_channel());
|
||||
channel->set_connected(false);
|
||||
}
|
||||
// ReleaseCaptureDevice
|
||||
vie_wrapper_->capture()->ReleaseCaptureDevice(capture_id_);
|
||||
@ -325,7 +345,6 @@ bool WebRtcVideoEngine::ReleaseCaptureDevice() {
|
||||
}
|
||||
|
||||
bool WebRtcVideoEngine::SetCaptureDevice(const Device* cam) {
|
||||
ASSERT(vie_wrapper_.get());
|
||||
ASSERT(cam != NULL);
|
||||
|
||||
ReleaseCaptureDevice();
|
||||
@ -333,47 +352,55 @@ bool WebRtcVideoEngine::SetCaptureDevice(const Device* cam) {
|
||||
webrtc::ViECapture* vie_capture = vie_wrapper_->capture();
|
||||
|
||||
// There's an external VCM
|
||||
if (capture_) {
|
||||
if (vie_capture->AllocateCaptureDevice(*capture_, capture_id_) != 0)
|
||||
if (capture_module_) {
|
||||
if (vie_capture->AllocateCaptureDevice(*capture_module_, 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];
|
||||
char device_name[256], device_id[256];
|
||||
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,
|
||||
if (vie_capture->GetCaptureDevice(i, device_name, sizeof(device_name),
|
||||
device_id, sizeof(device_id)) == 0) {
|
||||
// TODO: 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<char*>(device_name)) == 0) ||
|
||||
(cam->id.compare(reinterpret_cast<char*>(device_id)) == 0)) {
|
||||
if (cam->name.compare(device_name) == 0 ||
|
||||
cam->id.compare(device_id) == 0) {
|
||||
LOG(INFO) << "Found video capture device: " << device_name;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
if (!found) {
|
||||
return false;
|
||||
if (vie_capture->AllocateCaptureDevice(device_id, KMaxUniqueIdLength,
|
||||
capture_id_) != 0)
|
||||
}
|
||||
if (vie_capture->AllocateCaptureDevice(device_id, strlen(device_id),
|
||||
capture_id_) != 0) {
|
||||
ASSERT(capture_id_ == -1);
|
||||
}
|
||||
}
|
||||
|
||||
if (capture_id_ != -1) {
|
||||
// Connect to all the channels
|
||||
// Connect to all the channels if there is any.
|
||||
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());
|
||||
|
||||
// No channel should have been connected yet.
|
||||
// In case of switching device, all channel connections should have been
|
||||
// disconnected in ReleaseCaptureDevice() first.
|
||||
ASSERT(!channel->connected());
|
||||
|
||||
if (vie_capture->ConnectCaptureDevice(capture_id_,
|
||||
channel->video_channel()) == 0) {
|
||||
channel->set_connected(true);
|
||||
} else {
|
||||
LOG(LS_WARNING) << "SetCaptureDevice failed to ConnectCaptureDevice.";
|
||||
}
|
||||
}
|
||||
SetCapture(true);
|
||||
}
|
||||
@ -383,20 +410,31 @@ bool WebRtcVideoEngine::SetCaptureDevice(const Device* cam) {
|
||||
|
||||
bool WebRtcVideoEngine::SetCaptureModule(webrtc::VideoCaptureModule* vcm) {
|
||||
ReleaseCaptureDevice();
|
||||
if (capture_) {
|
||||
capture_->Release();
|
||||
if (capture_module_) {
|
||||
capture_module_->Release();
|
||||
capture_module_ = NULL;
|
||||
}
|
||||
capture_ = vcm;
|
||||
capture_->AddRef();
|
||||
external_capture_ = true;
|
||||
|
||||
if (vcm) {
|
||||
capture_module_ = vcm;
|
||||
capture_module_->AddRef();
|
||||
external_capture_ = true;
|
||||
} else {
|
||||
external_capture_ = false;
|
||||
}
|
||||
|
||||
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_);
|
||||
vie_wrapper_->render()->RemoveRenderer(capture_id_);
|
||||
// If the renderer already set, stop and remove it first
|
||||
if (vie_wrapper_->render()->StopRender(capture_id_) != 0) {
|
||||
LOG_RTCERR1(StopRender, capture_id_);
|
||||
}
|
||||
if (vie_wrapper_->render()->RemoveRenderer(capture_id_) != 0) {
|
||||
LOG_RTCERR1(RemoveRenderer, capture_id_);
|
||||
}
|
||||
}
|
||||
local_renderer_.reset(new WebRtcRenderAdapter(renderer));
|
||||
|
||||
@ -456,15 +494,36 @@ WebRtcVideoMediaChannel* WebRtcVideoEngine::CreateChannel(
|
||||
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;
|
||||
// Checks to see whether we comprehend and could receive a particular codec
|
||||
bool WebRtcVideoEngine::FindCodec(const VideoCodec& in) {
|
||||
for (int i = 0; i < ARRAY_SIZE(kVideoFormats); ++i) {
|
||||
const VideoFormat& fmt = kVideoFormats[i];
|
||||
if ((in.width == 0 && in.height == 0) ||
|
||||
(fmt.width == in.width && fmt.height == in.height)) {
|
||||
for (int j = 0; j < ARRAY_SIZE(kVideoCodecPrefs); ++j) {
|
||||
VideoCodec codec(kVideoCodecPrefs[j].payload_type,
|
||||
kVideoCodecPrefs[j].name, 0, 0, 0, 0);
|
||||
if (codec.Matches(in)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// SetDefaultCodec may be called while the capturer is running. For example, a
|
||||
// test call is started in a page with QVGA default codec, and then a real call
|
||||
// is started in another page with VGA default codec. This is the corner case
|
||||
// and happens only when a session is started. We ignore this case currently.
|
||||
bool WebRtcVideoEngine::SetDefaultCodec(const VideoCodec& codec) {
|
||||
if (!RebuildCodecList(codec)) {
|
||||
LOG(LS_WARNING) << "Failed to RebuildCodecList";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcVideoEngine::ConvertToCricketVideoCodec(
|
||||
const webrtc::VideoCodec& in_codec, VideoCodec& out_codec) {
|
||||
out_codec.id = in_codec.plType;
|
||||
@ -529,8 +588,24 @@ void WebRtcVideoEngine::UnregisterChannel(WebRtcVideoMediaChannel *channel) {
|
||||
}
|
||||
}
|
||||
|
||||
bool WebRtcVideoEngine::SetVoiceEngine(WebRtcVoiceEngine* voice_engine) {
|
||||
if (initialized_) {
|
||||
LOG(LS_WARNING) << "SetVoiceEngine can not be called after Init.";
|
||||
return false;
|
||||
}
|
||||
voice_engine_ = voice_engine;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WebRtcVideoEngine::EnableTimedRender() {
|
||||
if (initialized_) {
|
||||
LOG(LS_WARNING) << "EnableTimedRender can not be called after Init.";
|
||||
return false;
|
||||
}
|
||||
render_module_.reset(webrtc::VideoRender::CreateVideoRender(0, NULL,
|
||||
false, webrtc::kRenderExternal));
|
||||
return true;
|
||||
}
|
||||
|
||||
// WebRtcVideoMediaChannel
|
||||
|
||||
@ -540,6 +615,7 @@ WebRtcVideoMediaChannel::WebRtcVideoMediaChannel(
|
||||
voice_channel_(channel),
|
||||
vie_channel_(-1),
|
||||
sending_(false),
|
||||
connected_(false),
|
||||
render_started_(false),
|
||||
send_codec_(NULL) {
|
||||
engine->RegisterChannel(this);
|
||||
@ -554,6 +630,7 @@ bool WebRtcVideoMediaChannel::Init() {
|
||||
|
||||
LOG(LS_INFO) << "WebRtcVideoMediaChannel::Init "
|
||||
<< "video_channel " << vie_channel_ << " created";
|
||||
|
||||
// connect audio channel
|
||||
if (voice_channel_) {
|
||||
WebRtcVoiceMediaChannel* channel =
|
||||
@ -692,14 +769,27 @@ bool WebRtcVideoMediaChannel::SetSend(bool send) {
|
||||
LOG_RTCERR1(StartSend, vie_channel_);
|
||||
ret = false;
|
||||
}
|
||||
|
||||
// If the channel has not been connected to the capturer yet,
|
||||
// connect it now.
|
||||
if (!connected()) {
|
||||
if (engine()->video_engine()->capture()->ConnectCaptureDevice(
|
||||
engine()->capture_id(), vie_channel_) != 0) {
|
||||
LOG_RTCERR2(ConnectCaptureDevice, engine()->capture_id(), vie_channel_);
|
||||
ret = false;
|
||||
} else {
|
||||
set_connected(true);
|
||||
}
|
||||
}
|
||||
} else { // disable
|
||||
if (engine()->video_engine()->base()->StopSend(vie_channel_) != 0) {
|
||||
LOG_RTCERR1(StopSend, vie_channel_);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
if (ret)
|
||||
if (ret) {
|
||||
sending_ = send;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -718,9 +808,13 @@ bool WebRtcVideoMediaChannel::SetRenderer(
|
||||
if (ssrc != 0)
|
||||
return false;
|
||||
if (remote_renderer_.get()) {
|
||||
// If the renderer already set, stop it first
|
||||
engine_->video_engine()->render()->StopRender(vie_channel_);
|
||||
engine_->video_engine()->render()->RemoveRenderer(vie_channel_);
|
||||
// If the renderer already set, stop and remove it first
|
||||
if (engine_->video_engine()->render()->StopRender(vie_channel_) != 0) {
|
||||
LOG_RTCERR1(StopRender, vie_channel_);
|
||||
}
|
||||
if (engine_->video_engine()->render()->RemoveRenderer(vie_channel_) != 0) {
|
||||
LOG_RTCERR1(RemoveRenderer, vie_channel_);
|
||||
}
|
||||
}
|
||||
remote_renderer_.reset(new WebRtcRenderAdapter(renderer));
|
||||
|
||||
@ -906,7 +1000,6 @@ int WebRtcVideoMediaChannel::SendPacket(int channel, const void* data,
|
||||
if (!network_interface_) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
talk_base::Buffer packet(data, len, kMaxRtpPacketLen);
|
||||
return network_interface_->SendPacket(&packet) ? len : -1;
|
||||
}
|
||||
@ -923,5 +1016,5 @@ int WebRtcVideoMediaChannel::SendRTCPPacket(int channel,
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // HAVE_WEBRTC
|
||||
#endif // HAVE_WEBRTC_VIDEO
|
||||
|
||||
|
@ -1,197 +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_SESSION_PHONE_WEBRTCVIDEOENGINE_H_
|
||||
#define TALK_SESSION_PHONE_WEBRTCVIDEOENGINE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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<VideoCodec>& 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<CaptureResult> 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<WebRtcVideoMediaChannel*> VideoChannels;
|
||||
|
||||
talk_base::scoped_ptr<ViEWrapper> vie_wrapper_;
|
||||
webrtc::VideoCaptureModule* capture_;
|
||||
bool external_capture_;
|
||||
int capture_id_;
|
||||
webrtc::VideoRender* renderer_;
|
||||
WebRtcVoiceEngine* voice_engine_;
|
||||
std::vector<VideoCodec> video_codecs_;
|
||||
VideoChannels channels_;
|
||||
int log_level_;
|
||||
VideoEncoderConfig default_encoder_config_;
|
||||
bool capture_started_;
|
||||
talk_base::scoped_ptr<WebRtcRenderAdapter> 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<VideoCodec> &codecs);
|
||||
virtual bool SetSendCodecs(const std::vector<VideoCodec> &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<RtpHeaderExtension>& extensions) {
|
||||
return false;
|
||||
}
|
||||
virtual bool SetSendRtpHeaderExtensions(
|
||||
const std::vector<RtpHeaderExtension>& 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<webrtc::VideoCodec> send_codec_;
|
||||
talk_base::scoped_ptr<WebRtcRenderAdapter> remote_renderer_;
|
||||
};
|
||||
} // namespace cricket
|
||||
|
||||
#endif // TALK_SESSION_PHONE_WEBRTCVIDEOENGINE_H_
|
@ -1,238 +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/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
|
@ -1,97 +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_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_
|
@ -1,143 +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_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"
|
||||
#include "video_engine/main/interface/vie_errors.h"
|
||||
#include "video_engine/main/interface/vie_image_process.h"
|
||||
#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 cricket {
|
||||
|
||||
// all tracing macros should go to a common file
|
||||
|
||||
// automatically handles lifetime of VideoEngine
|
||||
class scoped_vie_engine {
|
||||
public:
|
||||
explicit scoped_vie_engine(webrtc::VideoEngine* e) : ptr(e) {}
|
||||
// VERIFY, to ensure that there are no leaks at shutdown
|
||||
~scoped_vie_engine() {
|
||||
if (ptr) {
|
||||
webrtc::VideoEngine::Delete(ptr);
|
||||
}
|
||||
}
|
||||
webrtc::VideoEngine* get() const { return ptr; }
|
||||
private:
|
||||
webrtc::VideoEngine* ptr;
|
||||
};
|
||||
|
||||
// scoped_ptr class to handle obtaining and releasing VideoEngine
|
||||
// interface pointers
|
||||
template<class T> class scoped_vie_ptr {
|
||||
public:
|
||||
explicit scoped_vie_ptr(const scoped_vie_engine& e)
|
||||
: ptr(T::GetInterface(e.get())) {}
|
||||
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:
|
||||
T* ptr;
|
||||
};
|
||||
|
||||
// Utility class for aggregating the various WebRTC interface.
|
||||
// Fake implementations can also be injected for testing.
|
||||
class ViEWrapper {
|
||||
public:
|
||||
ViEWrapper()
|
||||
: engine_(webrtc::VideoEngine::Create()),
|
||||
base_(engine_), codec_(engine_), capture_(engine_),
|
||||
network_(engine_), render_(engine_), rtp_(engine_),
|
||||
image_(engine_) {
|
||||
}
|
||||
|
||||
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),
|
||||
image_(image) {
|
||||
}
|
||||
|
||||
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_vie_engine engine_;
|
||||
scoped_vie_ptr<webrtc::ViEBase> base_;
|
||||
scoped_vie_ptr<webrtc::ViECodec> codec_;
|
||||
scoped_vie_ptr<webrtc::ViECapture> capture_;
|
||||
scoped_vie_ptr<webrtc::ViENetwork> network_;
|
||||
scoped_vie_ptr<webrtc::ViERender> render_;
|
||||
scoped_vie_ptr<webrtc::ViERTP_RTCP> rtp_;
|
||||
scoped_vie_ptr<webrtc::ViEImageProcess> image_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TALK_SESSION_PHONE_WEBRTCVIE_H_
|
@ -1,190 +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_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 T>
|
||||
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<webrtc::VoEAudioProcessing> processing_;
|
||||
scoped_voe_ptr<webrtc::VoEBase> base_;
|
||||
scoped_voe_ptr<webrtc::VoECodec> codec_;
|
||||
scoped_voe_ptr<webrtc::VoEDtmf> dtmf_;
|
||||
scoped_voe_ptr<webrtc::VoEFile> file_;
|
||||
scoped_voe_ptr<webrtc::VoEHardware> hw_;
|
||||
scoped_voe_ptr<webrtc::VoENetEqStats> neteq_;
|
||||
scoped_voe_ptr<webrtc::VoENetwork> network_;
|
||||
scoped_voe_ptr<webrtc::VoERTP_RTCP> rtp_;
|
||||
scoped_voe_ptr<webrtc::VoEVideoSync> sync_;
|
||||
scoped_voe_ptr<webrtc::VoEVolumeControl> 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_
|
@ -25,13 +25,8 @@
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// shhhhh{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
// shhhhh}
|
||||
|
||||
#ifdef HAVE_WEBRTC
|
||||
#ifdef HAVE_WEBRTC_VOICE
|
||||
|
||||
#include "talk/session/phone/webrtcvoiceengine.h"
|
||||
|
||||
@ -46,6 +41,7 @@
|
||||
#include "talk/base/helpers.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/stringencode.h"
|
||||
#include "talk/base/stringutils.h"
|
||||
#include "talk/session/phone/webrtcvoe.h"
|
||||
|
||||
#ifdef WIN32
|
||||
@ -77,9 +73,26 @@ 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
|
||||
// http://tools.ietf.org/html/draft-ietf-avtext-client-to-mixer-audio-level-03
|
||||
static const char kRtpAudioLevelHeaderExtension[] =
|
||||
"urn:ietf:params:rtp-hdrext:audio-level";
|
||||
"urn:ietf:params:rtp-hdrext:ssrc-audio-level";
|
||||
|
||||
static const char kIsacCodecName[] = "ISAC";
|
||||
static const char kL16CodecName[] = "L16";
|
||||
|
||||
// Dumps an AudioCodec in RFC 2327-ish format.
|
||||
static std::string ToString(const AudioCodec& codec) {
|
||||
std::stringstream ss;
|
||||
ss << codec.name << "/" << codec.clockrate << "/" << codec.channels
|
||||
<< " (" << codec.id << ")";
|
||||
return ss.str();
|
||||
}
|
||||
static std::string ToString(const webrtc::CodecInst& codec) {
|
||||
std::stringstream ss;
|
||||
ss << codec.plname << "/" << codec.plfreq << "/" << codec.channels
|
||||
<< " (" << codec.pltype << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static void LogMultiline(talk_base::LoggingSeverity sev, char* text) {
|
||||
const char* delim = "\r\n";
|
||||
@ -90,24 +103,19 @@ static void LogMultiline(talk_base::LoggingSeverity sev, char* text) {
|
||||
|
||||
// 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 },
|
||||
{ "ISAC", 16000, 103 },
|
||||
{ "ISAC", 32000, 104 },
|
||||
{ "speex", 16000, 107 },
|
||||
{ "G722", 16000, 9 },
|
||||
{ "ILBC", 8000, 102 },
|
||||
{ "speex", 8000, 108 },
|
||||
{ "PCMU", 8000, 0 },
|
||||
{ "PCMA", 8000, 8 },
|
||||
{ "CN", 32000, 106 },
|
||||
{ "CN", 16000, 105 },
|
||||
{ "CN", 8000, 13 },
|
||||
{ "red", 8000, 127 },
|
||||
{ "telephone-event", 8000, 126 },
|
||||
};
|
||||
|
||||
class WebRtcSoundclipMedia : public SoundclipMedia {
|
||||
@ -199,19 +207,6 @@ WebRtcVoiceEngine::WebRtcVoiceEngine()
|
||||
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)
|
||||
@ -227,16 +222,12 @@ WebRtcVoiceEngine::WebRtcVoiceEngine(VoEWrapper* voe_wrapper,
|
||||
}
|
||||
|
||||
void WebRtcVoiceEngine::Construct() {
|
||||
initialized_ = false;
|
||||
LOG(LS_VERBOSE) << "WebRtcVoiceEngine::WebRtcVoiceEngine";
|
||||
ApplyLogging();
|
||||
ApplyLogging("");
|
||||
if (tracing_->SetTraceCallback(this) == -1) {
|
||||
LOG_RTCERR0(SetTraceCallback);
|
||||
}
|
||||
// Update reference counters for the external ADM(s).
|
||||
if (adm_)
|
||||
adm_->AddRef();
|
||||
if (adm_sc_)
|
||||
adm_sc_->AddRef();
|
||||
|
||||
if (voe_wrapper_->base()->RegisterVoiceEngineObserver(*this) == -1) {
|
||||
LOG_RTCERR0(RegisterVoiceEngineObserver);
|
||||
@ -244,24 +235,48 @@ void WebRtcVoiceEngine::Construct() {
|
||||
// Clear the default agc state.
|
||||
memset(&default_agc_config_, 0, sizeof(default_agc_config_));
|
||||
|
||||
// Load our audio codec list
|
||||
// Load our audio codec list.
|
||||
ConstructCodecs();
|
||||
}
|
||||
|
||||
void WebRtcVoiceEngine::ConstructCodecs() {
|
||||
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;
|
||||
webrtc::CodecInst voe_codec;
|
||||
if (voe_wrapper_->codec()->GetCodec(i, voe_codec) != -1) {
|
||||
// Skip uncompressed formats.
|
||||
if (_stricmp(voe_codec.plname, kL16CodecName) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const CodecPref* pref = NULL;
|
||||
for (size_t j = 0; j < ARRAY_SIZE(kCodecPrefs); ++j) {
|
||||
if (_stricmp(kCodecPrefs[j].name, voe_codec.plname) == 0 &&
|
||||
kCodecPrefs[j].clockrate == voe_codec.plfreq) {
|
||||
pref = &kCodecPrefs[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pref) {
|
||||
// Use the payload type that we've configured in our pref table;
|
||||
// use the offset in our pref table to determine the sort order.
|
||||
AudioCodec codec(pref->payload_type, voe_codec.plname, voe_codec.plfreq,
|
||||
voe_codec.rate, voe_codec.channels,
|
||||
ARRAY_SIZE(kCodecPrefs) - (pref - kCodecPrefs));
|
||||
LOG(LS_INFO) << ToString(codec);
|
||||
// For ISAC, use 0 to indicate auto bandwidth in our signaling.
|
||||
if (_stricmp(codec.name.c_str(), kIsacCodecName) == 0) {
|
||||
codec.bitrate = 0;
|
||||
}
|
||||
codecs_.push_back(codec);
|
||||
} else {
|
||||
LOG(LS_WARNING) << "Unexpected codec: " << ToString(voe_codec);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure they are in local preference order
|
||||
// Make sure they are in local preference order.
|
||||
std::sort(codecs_.begin(), codecs_.end(), &AudioCodec::Preferable);
|
||||
}
|
||||
|
||||
@ -301,18 +316,17 @@ bool WebRtcVoiceEngine::InitInternal() {
|
||||
int old_level = log_level_;
|
||||
log_level_ = talk_base::_min(log_level_,
|
||||
static_cast<int>(talk_base::LS_INFO));
|
||||
ApplyLogging();
|
||||
ApplyLogging("");
|
||||
|
||||
// Init WebRtc VoiceEngine, enabling AEC logging if specified in SetLogging,
|
||||
// and install the externally provided (and implemented) ADM.
|
||||
// Init WebRtc VoiceEngine, enabling AEC logging if specified in SetLogging.
|
||||
if (voe_wrapper_->base()->Init(adm_) == -1) {
|
||||
LOG_RTCERR0_EX(Init, voe_wrapper_->error());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore the previous log level
|
||||
// Restore the previous log level and apply the log filter.
|
||||
log_level_ = old_level;
|
||||
ApplyLogging();
|
||||
ApplyLogging(log_filter_);
|
||||
|
||||
// Log the VoiceEngine version info
|
||||
char buffer[1024] = "";
|
||||
@ -322,7 +336,8 @@ bool WebRtcVoiceEngine::InitInternal() {
|
||||
|
||||
// Turn on AEC and AGC by default.
|
||||
if (!SetOptions(
|
||||
MediaEngine::ECHO_CANCELLATION | MediaEngine::AUTO_GAIN_CONTROL)) {
|
||||
MediaEngineInterface::ECHO_CANCELLATION |
|
||||
MediaEngineInterface::AUTO_GAIN_CONTROL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -332,33 +347,11 @@ bool WebRtcVoiceEngine::InitInternal() {
|
||||
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<AudioCodec>::const_iterator it = codecs_.begin();
|
||||
it != codecs_.end(); ++it) {
|
||||
LOG(LS_INFO) << it->name << "/" << it->clockrate << "/"
|
||||
<< it->channels << " " << it->id;
|
||||
LOG(LS_INFO) << ToString(*it);
|
||||
}
|
||||
|
||||
#if defined(LINUX) && !defined(HAVE_LIBPULSE)
|
||||
@ -366,7 +359,6 @@ bool WebRtcVoiceEngine::InitInternal() {
|
||||
#endif
|
||||
|
||||
// Initialize the VoiceEngine instance that we'll use to play out sound clips.
|
||||
// Also, install the externally provided (and implemented) ADM.
|
||||
if (voe_wrapper_sc_->base()->Init(adm_sc_) == -1) {
|
||||
LOG_RTCERR0_EX(Init, voe_wrapper_sc_->error());
|
||||
return false;
|
||||
@ -374,7 +366,7 @@ bool WebRtcVoiceEngine::InitInternal() {
|
||||
|
||||
// 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.
|
||||
// TODO: 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 &&
|
||||
@ -390,11 +382,13 @@ bool WebRtcVoiceEngine::InitInternal() {
|
||||
}
|
||||
#endif
|
||||
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcVoiceEngine::Terminate() {
|
||||
LOG(LS_INFO) << "WebRtcVoiceEngine::Terminate";
|
||||
initialized_ = false;
|
||||
|
||||
if (is_dumping_aec_) {
|
||||
if (voe_wrapper_->processing()->StopDebugRecording() == -1) {
|
||||
@ -405,12 +399,11 @@ void WebRtcVoiceEngine::Terminate() {
|
||||
|
||||
voe_wrapper_sc_->base()->Terminate();
|
||||
voe_wrapper_->base()->Terminate();
|
||||
|
||||
desired_local_monitor_enable_ = false;
|
||||
}
|
||||
|
||||
int WebRtcVoiceEngine::GetCapabilities() {
|
||||
return MediaEngine::AUDIO_SEND | MediaEngine::AUDIO_RECV;
|
||||
return AUDIO_SEND | AUDIO_RECV;
|
||||
}
|
||||
|
||||
VoiceMediaChannel *WebRtcVoiceEngine::CreateChannel() {
|
||||
@ -432,29 +425,58 @@ SoundclipMedia *WebRtcVoiceEngine::CreateSoundclip() {
|
||||
}
|
||||
|
||||
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
|
||||
// NS and typing detection are always on, if supported.
|
||||
bool aec = (options & MediaEngineInterface::ECHO_CANCELLATION) ? true : false;
|
||||
bool agc = (options & MediaEngineInterface::AUTO_GAIN_CONTROL) ? true : false;
|
||||
#if !defined(IOS) && !defined(ANDROID)
|
||||
if (voe_wrapper_->processing()->SetEcStatus(aec) == -1) {
|
||||
#endif
|
||||
LOG_RTCERR1(SetEcStatus, aec);
|
||||
return false;
|
||||
}
|
||||
// TODO (perkj):
|
||||
// This sets the AGC to use digital AGC since analog AGC can't be supported on
|
||||
// Chromium at the moment. Change back to analog when it can.
|
||||
if (voe_wrapper_->processing()->SetAgcStatus(
|
||||
agc, webrtc::kAgcAdaptiveDigital) == -1) {
|
||||
|
||||
if (voe_wrapper_->processing()->SetAgcStatus(agc) == -1) {
|
||||
LOG_RTCERR1(SetAgcStatus, agc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (voe_wrapper_->processing()->SetNsStatus(true) == -1) {
|
||||
LOG_RTCERR1(SetNsStatus, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (voe_wrapper_->processing()->SetTypingDetectionStatus(true) == -1) {
|
||||
// In case of error, log the info and continue
|
||||
LOG_RTCERR1(SetTypingDetectionStatus, true);
|
||||
}
|
||||
#else
|
||||
if (voe_wrapper_->processing()->SetEcStatus(aec, kEcAecm) == -1) {
|
||||
LOG_RTCERR2(SetEcStatus, aec, kEcAecm);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aec) {
|
||||
// Use speakerphone mode with comfort noise generation for mobile.
|
||||
if (voe_wrapper_->processing()->SetAecmMode(kAecmSpeakerphone, true) != 0) {
|
||||
LOG_RTCERR2(SetAecmMode, kAecmSpeakerphone, true);
|
||||
}
|
||||
}
|
||||
|
||||
// On mobile, GIPS recommends fixed AGC (not adaptive)
|
||||
if (voe_wrapper_->processing()->SetAgcStatus(agc, kAgcFixedDigital) == -1) {
|
||||
LOG_RTCERR2(SetAgcStatus, agc, kAgcFixedDigital);
|
||||
return false;
|
||||
}
|
||||
|
||||
// On mobile, GIPS recommends moderate aggressiveness.
|
||||
if (voe_wrapper_->processing()->SetNsStatus(true,
|
||||
kNsModerateSuppression) == -1) {
|
||||
LOG_RTCERR2(SetNsStatus, ns, kNsModerateSuppression);
|
||||
return false;
|
||||
}
|
||||
|
||||
// No typing detection support on iOS or Android.
|
||||
#endif // !IOS && !ANDROID
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -470,7 +492,7 @@ struct ResumeEntry {
|
||||
SendFlags send;
|
||||
};
|
||||
|
||||
// TODO(juberti): Refactor this so that the core logic can be used to set the
|
||||
// TODO: 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) {
|
||||
@ -689,21 +711,29 @@ bool WebRtcVoiceEngine::FindCodec(const AudioCodec& in) {
|
||||
return FindWebRtcCodec(in, NULL);
|
||||
}
|
||||
|
||||
// Get the VoiceEngine codec that matches |in|, with the supplied settings.
|
||||
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);
|
||||
webrtc::CodecInst voe_codec;
|
||||
if (voe_wrapper_->codec()->GetCodec(i, voe_codec) != -1) {
|
||||
AudioCodec codec(voe_codec.pltype, voe_codec.plname, voe_codec.plfreq,
|
||||
voe_codec.rate, voe_codec.channels, 0);
|
||||
// Allow arbitrary rates for ISAC to be specified.
|
||||
if (_stricmp(codec.name.c_str(), kIsacCodecName) == 0) {
|
||||
codec.bitrate = 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;
|
||||
// Fixup the payload type.
|
||||
voe_codec.pltype = in.id;
|
||||
// If ISAC is being used, and an explicit bitrate is not specified,
|
||||
// enable auto bandwidth adjustment.
|
||||
if (_stricmp(codec.name.c_str(), kIsacCodecName) == 0) {
|
||||
voe_codec.rate = (in.bitrate > 0) ? in.bitrate : -1;
|
||||
}
|
||||
*out = gcodec;
|
||||
*out = voe_codec;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -712,6 +742,19 @@ bool WebRtcVoiceEngine::FindWebRtcCodec(const AudioCodec& in,
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
log_filter_ = filter;
|
||||
ApplyLogging(initialized_ ? log_filter_ : "");
|
||||
}
|
||||
|
||||
int WebRtcVoiceEngine::GetLastEngineError() {
|
||||
return voe_wrapper_->error();
|
||||
}
|
||||
|
||||
// We suppport three different logging settings for VoiceEngine:
|
||||
// 1. Observer callback that goes into talk diagnostic logfile.
|
||||
// Use --logfile and --loglevel
|
||||
@ -724,19 +767,24 @@ bool WebRtcVoiceEngine::FindWebRtcCodec(const AudioCodec& in,
|
||||
//
|
||||
// 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;
|
||||
void WebRtcVoiceEngine::ApplyLogging(const std::string& log_filter) {
|
||||
// Set log level.
|
||||
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);
|
||||
|
||||
// voice log level
|
||||
ApplyLogging();
|
||||
|
||||
// Set encrypted trace file.
|
||||
std::vector<std::string> opts;
|
||||
talk_base::tokenize(filter, ' ', &opts);
|
||||
|
||||
// voice log file
|
||||
talk_base::tokenize(log_filter, ' ', '"', '"', &opts);
|
||||
std::vector<std::string>::iterator tracefile =
|
||||
std::find(opts.begin(), opts.end(), "tracefile");
|
||||
if (tracefile != opts.end() && ++tracefile != opts.end()) {
|
||||
@ -747,7 +795,7 @@ void WebRtcVoiceEngine::SetLogging(int min_sev, const char* filter) {
|
||||
}
|
||||
}
|
||||
|
||||
// AEC dump file
|
||||
// Set AEC dump file
|
||||
std::vector<std::string>::iterator recordEC =
|
||||
std::find(opts.begin(), opts.end(), "recordEC");
|
||||
if (recordEC != opts.end()) {
|
||||
@ -770,36 +818,18 @@ void WebRtcVoiceEngine::SetLogging(int min_sev, const char* filter) {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
"GetRemoteRTCPData() failed to measure statistics dueto lack of received RTP and/or RTCP packets", // NOLINT
|
||||
"GetRemoteRTCPData() failed to retrieve sender info for remoteside",
|
||||
"GetRTPStatistics() failed to measure RTT since noRTP 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",
|
||||
"GetRTPStatistics() failed to retrieve RTT fromthe RTP/RTCP module",
|
||||
"webrtc::RTCPReceiver::SenderInfoReceived No received SR",
|
||||
"StatisticsRTP() no statisitics availble",
|
||||
NULL
|
||||
};
|
||||
@ -852,16 +882,6 @@ void WebRtcVoiceEngine::CallbackOnError(const int channel_num,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@ -962,6 +982,32 @@ bool WebRtcVoiceEngine::SetConferenceMode(bool enable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcVoiceEngine::SetAudioDeviceModule(webrtc::AudioDeviceModule* adm,
|
||||
webrtc::AudioDeviceModule* adm_sc) {
|
||||
if (initialized_) {
|
||||
LOG(LS_WARNING) << "SetAudioDeviceModule can not be called after Init.";
|
||||
return false;
|
||||
}
|
||||
if (adm_) {
|
||||
adm_->Release();
|
||||
adm_ = NULL;
|
||||
}
|
||||
if (adm) {
|
||||
adm_ = adm;
|
||||
adm_->AddRef();
|
||||
}
|
||||
|
||||
if (adm_sc_) {
|
||||
adm_sc_->Release();
|
||||
adm_sc_ = NULL;
|
||||
}
|
||||
if (adm_sc) {
|
||||
adm_sc_ = adm_sc;
|
||||
adm_sc_->AddRef();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// WebRtcVoiceMediaChannel
|
||||
WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel(WebRtcVoiceEngine *engine)
|
||||
: WebRtcMediaChannel<VoiceMediaChannel, WebRtcVoiceEngine>(
|
||||
@ -989,6 +1035,9 @@ WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel(WebRtcVoiceEngine *engine)
|
||||
|
||||
// Create a random but nonzero send SSRC
|
||||
SetSendSsrc(talk_base::CreateRandomNonZeroId());
|
||||
|
||||
// Reset all recv codecs; they will be enabled via SetRecvCodecs.
|
||||
ResetRecvCodecs(voe_channel());
|
||||
}
|
||||
|
||||
WebRtcVoiceMediaChannel::~WebRtcVoiceMediaChannel() {
|
||||
@ -1031,26 +1080,22 @@ bool WebRtcVoiceMediaChannel::SetOptions(int flags) {
|
||||
|
||||
bool WebRtcVoiceMediaChannel::SetRecvCodecs(
|
||||
const std::vector<AudioCodec>& 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.
|
||||
// Set the payload types to be used for incoming media.
|
||||
bool ret = true;
|
||||
for (std::vector<AudioCodec>::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;
|
||||
}
|
||||
LOG(LS_INFO) << "Setting receive voice codecs:";
|
||||
for (std::vector<AudioCodec>::const_iterator it = codecs.begin();
|
||||
it != codecs.end() && ret; ++it) {
|
||||
webrtc::CodecInst voe_codec;
|
||||
if (engine()->FindWebRtcCodec(*it, &voe_codec)) {
|
||||
LOG(LS_INFO) << ToString(*it);
|
||||
voe_codec.pltype = it->id;
|
||||
if (engine()->voe()->codec()->SetRecPayloadType(
|
||||
voe_channel(), voe_codec) == -1) {
|
||||
LOG_RTCERR2(SetRecPayloadType, voe_channel(), ToString(voe_codec));
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
LOG(LS_WARNING) << "Unknown codec " << i->name;
|
||||
LOG(LS_WARNING) << "Unknown codec " << ToString(*it);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
@ -1071,29 +1116,30 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
|
||||
webrtc::CodecInst send_codec;
|
||||
memset(&send_codec, 0, sizeof(send_codec));
|
||||
|
||||
for (std::vector<AudioCodec>::const_iterator i = codecs.begin();
|
||||
i != codecs.end(); ++i) {
|
||||
for (std::vector<AudioCodec>::const_iterator it = codecs.begin();
|
||||
it != codecs.end(); ++it) {
|
||||
// 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;
|
||||
webrtc::CodecInst voe_codec;
|
||||
if (!engine()->FindWebRtcCodec(*it, &voe_codec)) {
|
||||
LOG(LS_WARNING) << "Unknown codec " << ToString(voe_codec);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the DTMF telephone event "codec" and tell VoiceEngine about it.
|
||||
if (i->name == "telephone-event" || i->name == "audio/telephone-event") {
|
||||
if (_stricmp(it->name.c_str(), "telephone-event") == 0 ||
|
||||
_stricmp(it->name.c_str(), "audio/telephone-event") == 0) {
|
||||
engine()->voe()->dtmf()->SetSendTelephoneEventPayloadType(
|
||||
voe_channel(), i->id);
|
||||
voe_channel(), it->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") {
|
||||
if (_stricmp(it->name.c_str(), "CN") == 0) {
|
||||
webrtc::PayloadFrequencies cn_freq;
|
||||
switch (i->clockrate) {
|
||||
switch (it->clockrate) {
|
||||
case 8000:
|
||||
cn_freq = webrtc::kFreq8000Hz;
|
||||
break;
|
||||
@ -1104,14 +1150,14 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
|
||||
cn_freq = webrtc::kFreq32000Hz;
|
||||
break;
|
||||
default:
|
||||
LOG(LS_WARNING) << "CN frequency " << i->clockrate
|
||||
LOG(LS_WARNING) << "CN frequency " << it->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);
|
||||
it->id, cn_freq);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1120,24 +1166,23 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
|
||||
// "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") {
|
||||
if (_stricmp(it->name.c_str(), "red") == 0) {
|
||||
// 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)) {
|
||||
if (!GetRedSendCodec(*it, codecs, &send_codec)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Enable redundant encoding of the specified codec. Treat any
|
||||
// failure as a fatal internal error.
|
||||
LOG(LS_INFO) << "Enabling RED";
|
||||
LOG(LS_INFO) << "Enabling FEC";
|
||||
if (engine()->voe()->rtp()->SetFECStatus(voe_channel(),
|
||||
true, i->id) == -1) {
|
||||
LOG_RTCERR3(SetFECStatus, voe_channel(), true, i->id);
|
||||
true, it->id) == -1) {
|
||||
LOG_RTCERR3(SetFECStatus, voe_channel(), true, it->id);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
send_codec = gcodec;
|
||||
send_codec.pltype = i->id;
|
||||
send_codec = voe_codec;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
@ -1152,11 +1197,11 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
|
||||
}
|
||||
|
||||
// Set the codec.
|
||||
LOG(LS_INFO) << "Selected voice codec " << send_codec.plname
|
||||
<< "/" << send_codec.plfreq;
|
||||
LOG(LS_INFO) << "Selected voice codec " << ToString(send_codec)
|
||||
<< ", bitrate=" << send_codec.rate;
|
||||
if (engine()->voe()->codec()->SetSendCodec(voe_channel(),
|
||||
send_codec) == -1) {
|
||||
LOG_RTCERR1(SetSendCodec, voe_channel());
|
||||
send_codec) == -1) {
|
||||
LOG_RTCERR2(SetSendCodec, voe_channel(), ToString(send_codec));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1277,7 +1322,6 @@ bool WebRtcVoiceMediaChannel::ChangeSend(SendFlags send) {
|
||||
|
||||
// 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;
|
||||
@ -1390,6 +1434,24 @@ bool WebRtcVoiceMediaChannel::AddStream(uint32 ssrc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the same recv payload types as our default channel.
|
||||
ResetRecvCodecs(channel);
|
||||
int ncodecs = engine()->voe()->codec()->NumOfCodecs();
|
||||
for (int i = 0; i < ncodecs; ++i) {
|
||||
webrtc::CodecInst voe_codec;
|
||||
if (engine()->voe()->codec()->GetCodec(i, voe_codec) != -1) {
|
||||
voe_codec.rate = 0; // Needed to make GetRecPayloadType work for ISAC
|
||||
if (engine()->voe()->codec()->GetRecPayloadType(
|
||||
voe_channel(), voe_codec) != -1) {
|
||||
if (engine()->voe()->codec()->SetRecPayloadType(
|
||||
channel, voe_codec) == -1) {
|
||||
LOG_RTCERR2(SetRecPayloadType, channel, ToString(voe_codec));
|
||||
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
|
||||
@ -1403,7 +1465,7 @@ bool WebRtcVoiceMediaChannel::AddStream(uint32 ssrc) {
|
||||
|
||||
mux_channels_[ssrc] = channel;
|
||||
|
||||
// TODO(juberti): We should rollback the add if SetPlayout fails.
|
||||
// TODO: We should rollback the add if SetPlayout fails.
|
||||
LOG(LS_INFO) << "New audio stream " << ssrc
|
||||
<< " registered to VoiceEngine channel #"
|
||||
<< channel << ".";
|
||||
@ -1433,7 +1495,7 @@ bool WebRtcVoiceMediaChannel::RemoveStream(uint32 ssrc) {
|
||||
// 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?
|
||||
// TODO: Does the default channel still have it's CN state?
|
||||
LOG(LS_INFO) << "Enabling playback on the default voice channel";
|
||||
SetPlayout(voe_channel(), true);
|
||||
}
|
||||
@ -1465,6 +1527,88 @@ int WebRtcVoiceMediaChannel::GetOutputLevel() {
|
||||
return highest;
|
||||
}
|
||||
|
||||
|
||||
bool WebRtcVoiceMediaChannel::SetOutputScaling(
|
||||
uint32 ssrc, double left, double right) {
|
||||
talk_base::CritScope lock(&mux_channels_cs_);
|
||||
// Collect the channels to scale the output volume.
|
||||
std::vector<int> channels;
|
||||
if (0 == ssrc) { // Collect all channels, including the default one.
|
||||
channels.push_back(voe_channel());
|
||||
for (ChannelMap::const_iterator it = mux_channels_.begin();
|
||||
it != mux_channels_.end(); ++it) {
|
||||
channels.push_back(it->second);
|
||||
}
|
||||
} else { // Collect only the channel of the specified ssrc.
|
||||
int channel = GetChannel(ssrc);
|
||||
if (-1 == channel) {
|
||||
LOG(LS_WARNING) << "Cannot find channel for ssrc:" << ssrc;
|
||||
return false;
|
||||
}
|
||||
channels.push_back(channel);
|
||||
}
|
||||
|
||||
// Scale the output volume for the collected channels. We first normalize to
|
||||
// scale the volume and then set the left and right pan.
|
||||
float scale = static_cast<float>(talk_base::_max(left, right));
|
||||
if (scale > 0.0001f) {
|
||||
left /= scale;
|
||||
right /= scale;
|
||||
}
|
||||
for (std::vector<int>::const_iterator it = channels.begin();
|
||||
it != channels.end(); ++it) {
|
||||
if (-1 == engine()->voe()->volume()->SetChannelOutputVolumeScaling(
|
||||
*it, scale)) {
|
||||
LOG_RTCERR2(SetChannelOutputVolumeScaling, *it, scale);
|
||||
return false;
|
||||
}
|
||||
if (-1 == engine()->voe()->volume()->SetOutputVolumePan(
|
||||
*it, static_cast<float>(left), static_cast<float>(right))) {
|
||||
LOG_RTCERR3(SetOutputVolumePan, *it, left, right);
|
||||
// Do not return if fails. SetOutputVolumePan is not available for all
|
||||
// pltforms.
|
||||
}
|
||||
LOG(LS_INFO) << "SetOutputScaling to left=" << left * scale
|
||||
<< " right=" << right * scale
|
||||
<< " for channel " << *it << " and ssrc " << ssrc;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcVoiceMediaChannel::GetOutputScaling(
|
||||
uint32 ssrc, double* left, double* right) {
|
||||
if (!left || !right) return false;
|
||||
|
||||
talk_base::CritScope lock(&mux_channels_cs_);
|
||||
// Determine which channel based on ssrc.
|
||||
int channel = (0 == ssrc) ? voe_channel() : GetChannel(ssrc);
|
||||
if (channel == -1) {
|
||||
LOG(LS_WARNING) << "Cannot find channel for ssrc:" << ssrc;
|
||||
return false;
|
||||
}
|
||||
|
||||
float scaling;
|
||||
if (-1 == engine()->voe()->volume()->GetChannelOutputVolumeScaling(
|
||||
channel, scaling)) {
|
||||
LOG_RTCERR2(GetChannelOutputVolumeScaling, channel, scaling);
|
||||
return false;
|
||||
}
|
||||
|
||||
float left_pan;
|
||||
float right_pan;
|
||||
if (-1 == engine()->voe()->volume()->GetOutputVolumePan(
|
||||
channel, left_pan, right_pan)) {
|
||||
LOG_RTCERR3(GetOutputVolumePan, channel, left_pan, right_pan);
|
||||
// If GetOutputVolumePan fails, we use the default left and right pan.
|
||||
left_pan = 1.0f;
|
||||
right_pan = 1.0f;
|
||||
}
|
||||
|
||||
*left = scaling * left_pan;
|
||||
*right = scaling * right_pan;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcVoiceMediaChannel::SetRingbackTone(const char *buf, int len) {
|
||||
ringback_tone_.reset(new WebRtcSoundclipStream(buf, len));
|
||||
return true;
|
||||
@ -1601,7 +1745,7 @@ 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.
|
||||
// TODO: Remove this workaround.
|
||||
webrtc::CallStatistics cs;
|
||||
unsigned int ssrc;
|
||||
webrtc::CodecInst codec;
|
||||
@ -1643,7 +1787,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) {
|
||||
sinfo.fraction_lost = -1;
|
||||
sinfo.jitter_ms = -1;
|
||||
}
|
||||
// TODO(juberti): Figure out how to get remote packets_lost, ext_seqnum
|
||||
// TODO: Figure out how to get remote packets_lost, ext_seqnum
|
||||
sinfo.packets_lost = -1;
|
||||
sinfo.ext_seqnum = -1;
|
||||
|
||||
@ -1801,7 +1945,6 @@ bool WebRtcVoiceMediaChannel::GetRedSendCodec(const AudioCodec& red_codec,
|
||||
// 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;
|
||||
@ -1815,13 +1958,29 @@ bool WebRtcVoiceMediaChannel::EnableRtcp(int channel) {
|
||||
LOG_RTCERR2(SetRTCPStatus, voe_channel(), 1);
|
||||
return false;
|
||||
}
|
||||
// TODO(juberti): Enable VQMon and RTCP XR reports, once we know what
|
||||
// TODO: 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::ResetRecvCodecs(int channel) {
|
||||
int ncodecs = engine()->voe()->codec()->NumOfCodecs();
|
||||
for (int i = 0; i < ncodecs; ++i) {
|
||||
webrtc::CodecInst voe_codec;
|
||||
if (engine()->voe()->codec()->GetCodec(i, voe_codec) != -1) {
|
||||
voe_codec.pltype = -1;
|
||||
if (engine()->voe()->codec()->SetRecPayloadType(
|
||||
channel, voe_codec) == -1) {
|
||||
LOG_RTCERR2(SetRecPayloadType, channel, ToString(voe_codec));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcVoiceMediaChannel::SetPlayout(int channel, bool playout) {
|
||||
if (playout) {
|
||||
LOG(LS_INFO) << "Starting playout for channel #" << channel;
|
||||
@ -1893,4 +2052,4 @@ int WebRtcSoundclipStream::Rewind() {
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // HAVE_WEBRTC
|
||||
#endif // HAVE_WEBRTC_VOICE
|
||||
|
@ -1,320 +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_SESSION_PHONE_WEBRTCVOICEENGINE_H_
|
||||
#define TALK_SESSION_PHONE_WEBRTCVOICEENGINE_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<AudioCodec>& 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<WebRtcSoundclipMedia *> SoundclipList;
|
||||
typedef std::vector<WebRtcVoiceMediaChannel *> 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<VoEWrapper> voe_wrapper_;
|
||||
// A secondary instance, for playing out soundclips (on the 'ring' device).
|
||||
talk_base::scoped_ptr<VoEWrapper> voe_wrapper_sc_;
|
||||
talk_base::scoped_ptr<VoETraceWrapper> tracing_;
|
||||
// The external audio device manager
|
||||
webrtc::AudioDeviceModule* adm_;
|
||||
webrtc::AudioDeviceModule* adm_sc_;
|
||||
int log_level_;
|
||||
bool is_dumping_aec_;
|
||||
std::vector<AudioCodec> codecs_;
|
||||
bool desired_local_monitor_enable_;
|
||||
talk_base::scoped_ptr<WebRtcMonitorStream> 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 T, class E>
|
||||
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<VoiceMediaChannel,
|
||||
WebRtcVoiceEngine> {
|
||||
public:
|
||||
explicit WebRtcVoiceMediaChannel(WebRtcVoiceEngine *engine);
|
||||
virtual ~WebRtcVoiceMediaChannel();
|
||||
virtual bool SetOptions(int options);
|
||||
virtual bool SetRecvCodecs(const std::vector<AudioCodec> &codecs);
|
||||
virtual bool SetSendCodecs(const std::vector<AudioCodec> &codecs);
|
||||
virtual bool SetRecvRtpHeaderExtensions(
|
||||
const std::vector<RtpHeaderExtension>& extensions);
|
||||
virtual bool SetSendRtpHeaderExtensions(
|
||||
const std::vector<RtpHeaderExtension>& 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<AudioCodec>& 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<uint32, int> ChannelMap;
|
||||
talk_base::scoped_ptr<WebRtcSoundclipStream> ringback_tone_;
|
||||
std::set<int> 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_
|
Loading…
x
Reference in New Issue
Block a user