git-svn-id: http://webrtc.googlecode.com/svn/trunk@353 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
mallinath@google.com 2011-08-11 20:44:56 +00:00
parent 16f1dfc953
commit 424e76a8c1
2 changed files with 519 additions and 0 deletions

View File

@ -0,0 +1,331 @@
/*
* 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/webrtcsessionchannel.h"
#include "talk/app/webrtc/stream_dev.h"
#include "talk/app/webrtc/webrtc_json_dev.h"
#include "talk/base/logging.h"
#include "talk/base/thread.h"
#include "talk/p2p/base/transportchannel.h"
#include "talk/p2p/base/session.h"
#include "talk/p2p/base/sessiondescription.h"
#include "talk/session/phone/channel.h"
#include "talk/session/phone/channelmanager.h"
#include "talk/session/phone/codec.h"
#include "talk/session/phone/mediasessionclient.h"
namespace webrtc {
enum {
MSG_WEBRTC_SENDSIGNAL = 1,
MSG_WEBRTC_STATECHANGE,
};
static const char* direction_str[] = {
"sendonly",
"recvonly",
"sendrecv",
"inactive"
};
typedef std::vector<cricket::AudioCodec> AudioCodecs;
typedef std::vector<cricket::VideoCodec> VideoCodecs;
struct SendSignalMsgParams : public talk_base::MessageData {
SendSignalMsgParams(const std::vector<cricket::Candidate> candidates)
: candidates_(candidates) {
}
std::vector<cricket::Candidate> candidates_;
};
// TODO(mallinath) - Handling of RTCP packets when remote end point doesn't
// support RTCP muxing.
WebRtcSessionChannel::WebRtcSessionChannel(MediaStreamTrack* track,
cricket::ChannelManager* cmgr,
talk_base::Thread* signal_thread)
: video_(false),
transport_channel_name_(),
enabled_(false),
media_channel_(NULL),
media_stream_track_(track),
channel_manager_(cmgr),
direction_(SD_SENDRECV),
signaling_thread_(signal_thread),
state_(STATE_INIT) {
if (track->kind().compare(kVideoTrackKind) == 0) {
video_ = true;
}
// TODO(mallinath) Register "this" object with track to get OnChanged event.
}
WebRtcSessionChannel::~WebRtcSessionChannel() {
}
void WebRtcSessionChannel::OnChanged() {
enabled_ = !enabled_;
media_channel_->Enable(enabled_);
}
bool WebRtcSessionChannel::Initialize(cricket::BaseSession* session) {
// By default RTCP muxing is enabled on, rtcp flag is set to false
// on cricket::BaseChannel.
if (video_) {
media_channel_.reset(channel_manager_->CreateVideoChannel(
session, media_stream_track_->label(), false, NULL));
transport_channel_name_ = "video_rtp";
} else {
media_channel_.reset(channel_manager_->CreateVoiceChannel(
session, media_stream_track_->label(), false));
transport_channel_name_ = "rtp";
}
ASSERT(!media_channel_.get());
return true;
}
bool WebRtcSessionChannel::EnableMediaChannel(bool enable) {
enabled_ = enable;
return media_channel_->Enable(enable);
}
cricket::SessionDescription* WebRtcSessionChannel::GetChannelMediaDesc() {
cricket::SessionDescription* sdp =
new cricket::SessionDescription();
if (video_) {
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);
}
video->SortCodecs();
// Enable RTCP muxing with RTP port
video->set_rtcp_mux(true);
sdp->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);
}
audio->SortCodecs();
// Enable RTCP muxing with RTP port
audio->set_rtcp_mux(true);
sdp->AddContent(cricket::CN_AUDIO, cricket::NS_JINGLE_RTP, audio);
}
return sdp;
}
void WebRtcSessionChannel::SendSignalingMessage(
const std::vector<cricket::Candidate>& candidates) {
SendSignalMsgParams* msg_param = new SendSignalMsgParams(candidates);
signaling_thread_->Post(this, MSG_WEBRTC_SENDSIGNAL, msg_param);
}
void WebRtcSessionChannel::SendSignalingMessage_s(
const std::vector<cricket::Candidate>& candidates) {
cricket::SessionDescription* sdp = GetChannelMediaDesc();
ASSERT(sdp);
std::string signaling_message;
if (GetSignalingMessage(sdp,
candidates,
video_,
media_stream_track_->label(),
direction_str[direction_],
&signaling_message)) {
set_local_description(sdp);
SignalJSONMessageReady(this, signaling_message);
if (state_ == STATE_INIT) {
SetState(STATE_SENTINITIATE);
} else {
SetState(STATE_SENDRECV);
}
}
// TODO(mallinath) - Handling on error
}
void WebRtcSessionChannel::SetState(State state) {
if (state != state) {
state_ = state;
signaling_thread_->Post(this, MSG_WEBRTC_STATECHANGE);
}
}
void WebRtcSessionChannel::OnStateChange() {
switch (state_) {
case STATE_SENTINITIATE:
case STATE_RECEIVING: {
// Don't do anything yet.
break;
}
case STATE_RECEIVEDINITIATE: {
SetState(STATE_SENTACCEPT);
break;
}
case STATE_SENTACCEPT: {
if (!SetLocalMediaContent(remote_description_, cricket::CA_OFFER)) {
LOG(LS_ERROR) << "Failure in SetLocalMediaContent with CA_OFFER";
SignalSessionChannelError(this, ERROR_CONTENT);
return;
}
SetState(STATE_RECEIVING);
break;
}
case STATE_RECEIVEDACCEPT: {
// Start sending
if (!SetRemoteMediaContent(remote_description_, cricket::CA_ANSWER)) {
LOG(LS_ERROR) << "Failure in SetRemoteMediaContent with CA_ANSWER";
SignalSessionChannelError(this, ERROR_CONTENT);
return;
}
SetState(STATE_SENDING);
break;
}
case STATE_SENDING: {
// Enable channel to start sending to peer
media_channel_->Enable(true);
break;
}
case STATE_SENDRECV: {
// Start sending
if (media_channel_->enabled() &&
!SetLocalMediaContent(remote_description_, cricket::CA_OFFER)) {
LOG(LS_ERROR) << "Failure in SetRemoteMediaContent with CA_ANSWER";
SignalSessionChannelError(this, ERROR_CONTENT);
return;
} else {
if (!SetRemoteMediaContent(local_description_, cricket::CA_ANSWER)) {
LOG(LS_ERROR) << "Failure in SetLocalmediaContent with CA_ANSWER";
SignalSessionChannelError(this, ERROR_CONTENT);
return;
}
media_channel_->Enable(true);
}
break;
}
default:
ASSERT(false);
break;
}
}
bool WebRtcSessionChannel::ProcessRemoteMessage(
cricket::SessionDescription* sdp) {
set_remote_description(sdp);
if (state_ == STATE_SENTINITIATE) {
SetState(STATE_RECEIVEDACCEPT);
} else if (state_ == STATE_INIT) {
SetState(STATE_RECEIVEDINITIATE);
} else if (state_ == STATE_SENDING) {
SetState(STATE_SENDRECV);
}
return true;
}
bool WebRtcSessionChannel::SetLocalMediaContent(
const cricket::SessionDescription* sdp,
cricket::ContentAction action) {
ASSERT(!media_channel_.get());
const cricket::MediaContentDescription* content = NULL;
content = GetFirstContent(sdp, video_);
if (content && !media_channel_->SetLocalContent(content, action)) {
LOG(LS_ERROR) << "Failure in SetLocaContent";
return false;
}
return true;
}
bool WebRtcSessionChannel::SetRemoteMediaContent(
const cricket::SessionDescription* sdp,
cricket::ContentAction action) {
ASSERT(!media_channel_.get());
const cricket::MediaContentDescription* content = NULL;
content = GetFirstContent(sdp, video_);
if (content && !media_channel_->SetRemoteContent(content, action)) {
LOG(LS_ERROR) << "Failure in SetRemoteContent";
return false;
}
return true;
}
const cricket::MediaContentDescription* WebRtcSessionChannel::GetFirstContent(
const cricket::SessionDescription* sdp,
bool video) {
const cricket::ContentInfo* cinfo = NULL;
if (video) {
cinfo = cricket::GetFirstVideoContent(sdp);
} else {
cinfo = cricket::GetFirstAudioContent(sdp);
}
if (cinfo == NULL) {
return NULL;
}
return static_cast<const cricket::MediaContentDescription*>(
cinfo->description);
}
void WebRtcSessionChannel::DestroyMediaChannel() {
ASSERT(media_channel_.get());
if (video_) {
cricket::VideoChannel* video_channel =
static_cast<cricket::VideoChannel*> (media_channel_.get());
channel_manager_->DestroyVideoChannel(video_channel);
} else {
cricket::VoiceChannel* voice_channel =
static_cast<cricket::VoiceChannel*> (media_channel_.get());
channel_manager_->DestroyVoiceChannel(voice_channel);
}
media_channel_.reset(NULL);
enabled_ = false;
}
void WebRtcSessionChannel::OnMessage(talk_base::Message* message) {
talk_base::MessageData* data = message->pdata;
switch (message->message_id) {
case MSG_WEBRTC_SENDSIGNAL: {
SendSignalMsgParams* p = static_cast<SendSignalMsgParams*>(data);
SendSignalingMessage_s(p->candidates_);
delete p;
break;
}
case MSG_WEBRTC_STATECHANGE: {
OnStateChange();
break;
}
default : {
ASSERT(false);
break;
}
}
}
} // namespace webrtc

View File

@ -0,0 +1,188 @@
/*
* 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_WEBRTCSESSIONCHANNEL_H_
#define TALK_APP_WEBRTC_WEBRTCSESSIONCHANNEL_H_
#include <string>
#include <vector>
#include "talk/app/webrtc/stream_dev.h"
#include "talk/base/messagehandler.h"
#include "talk/base/scoped_ptr.h"
#include "talk/base/sigslot.h"
#include "talk/p2p/base/candidate.h"
#include "talk/p2p/base/sessiondescription.h"
namespace talk_base {
class Thread;
}
namespace cricket {
class BaseChannel;
class ChannelManager;
class BaseSession;
class SessionDescription;
class MediaContentDescription;
}
namespace webrtc {
// NOTE: Channels are responsible for creating the JSON message for media
// stream. This was done to accommodate additional signaling attributes which
// are currenly not available in part of cricket::SessionDescription.
// One example is StreamDirection which will be added as "sendonly"
// "recvonly" "sendrecv" and "inactive".
// Another reason to create session channels is to support uni-directional
// stream management and these channels apply content to cricket::BaseChannel
// not through cricket::BaseSession::SetState.
//
// State transition at local and remote peer
// (Local) (Remote)
// INIT INIT
// | |
// SENTINITIATE (AddStream) RECEIVEDINITIATE (OnAddStream)
// | |
// RECEIVEDACCEPT (StartSend) SENTACCEPT (StartReceive)
// | |
// SENDING RECEIVING
// | |
// SENDRECV (OnAddStream,StartRecv) SENDRECV (AddStream, StartSend)
//
//
class WebRtcSessionChannel : public talk_base::MessageHandler,
public Observer {
public:
enum State {
STATE_INIT, // Channel Initialization state
STATE_SENTINITIATE, // After local AddStream (sendrecv)
STATE_SENTACCEPT, // Accepted incoming stream (recvonly)
STATE_RECEIVEDACCEPT, // Receives acceptance from remote (sendonly)
STATE_RECEIVEDINITIATE, // Initial stream request (onAddStream)
STATE_SENDING, // Starts sending media to remote
STATE_RECEIVING, // starts receiving media
STATE_SENDRECV, // Send and Recv from/to remote
STATE_INVALID, // Invalid state
};
enum StreamDirection {
SD_SENDONLY, // media stream is sendonly
SD_RECVONLY, // media stream is recvonly
SD_SENDRECV, // media stream is both sendrecv
SD_INACTIVE, // media stream is inactive
};
// From cricket::BaseSession
enum Error {
ERROR_NONE = 0, // no error
ERROR_CONTENT = 1, // channel errors in SetLocalContent/SetRemoteContent
};
WebRtcSessionChannel(MediaStreamTrack* track,
cricket::ChannelManager* channel_manager,
talk_base::Thread* signaling_thread);
virtual ~WebRtcSessionChannel();
bool Initialize(cricket::BaseSession* session);
void DestroyMediaChannel();
void OnChanged();
void set_enabled(bool enabled) {
enabled_ = enabled;
}
bool enabled() {
return enabled_;
}
// This will be called from WebRtcSession not from MediaStreamTrack
bool EnableMediaChannel(bool enable);
std::string name() {
return transport_channel_name_;
}
void set_transport_channel_name(const std::string& name) {
transport_channel_name_ = name;
}
MediaStreamTrack* media_stream_track() {
return media_stream_track_;
}
void SendSignalingMessage(
const std::vector<cricket::Candidate>& candidates);
sigslot::signal2<WebRtcSessionChannel*,
const std::string&> SignalJSONMessageReady;
sigslot::signal2<WebRtcSessionChannel*, Error> SignalSessionChannelError;
void SetState(State state);
bool ProcessRemoteMessage(cricket::SessionDescription* sdp);
void set_local_description(cricket::SessionDescription* sdesc) {
if (sdesc != local_description_) {
delete local_description_;
local_description_ = sdesc;
}
}
void set_remote_description(cricket::SessionDescription* sdesc) {
if (sdesc != remote_description_) {
delete remote_description_;
remote_description_ = sdesc;
}
}
private:
void OnMessage(talk_base::Message* message);
void OnStateChange();
// These two methods are used to set directly the media content description
// On BaseChannel, rather than going through BaseSession::SetState
// This will give us the flexibility when to send and receive the data
// based on AddStream
bool SetLocalMediaContent(const cricket::SessionDescription* sdp,
cricket::ContentAction action);
bool SetRemoteMediaContent(const cricket::SessionDescription* sdp,
cricket::ContentAction action);
cricket::SessionDescription* GetChannelMediaDesc();
void SendSignalingMessage_s(
const std::vector<cricket::Candidate>& candidates);
// methods from BaseChannel
const cricket::MediaContentDescription* GetFirstContent(
const cricket::SessionDescription* sdesc,
bool video);
bool video_;
std::string transport_channel_name_;
bool enabled_;
talk_base::scoped_ptr<cricket::BaseChannel> media_channel_;
MediaStreamTrack* media_stream_track_;
cricket::ChannelManager* channel_manager_;
StreamDirection direction_;
talk_base::Thread* signaling_thread_;
State state_;
const cricket::SessionDescription* local_description_;
cricket::SessionDescription* remote_description_;
DISALLOW_COPY_AND_ASSIGN(WebRtcSessionChannel);
};
} // namspace webrtc
#endif // TALK_APP_WEBRTC_WEBRTCSESSIONCHANNEL_H_