2013-07-10 02:45:36 +02:00
|
|
|
/*
|
|
|
|
* libjingle
|
|
|
|
* Copyright 2004 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/media/base/hybridvideoengine.h"
|
|
|
|
|
|
|
|
#include "talk/base/logging.h"
|
|
|
|
|
|
|
|
namespace cricket {
|
|
|
|
|
|
|
|
HybridVideoMediaChannel::HybridVideoMediaChannel(
|
|
|
|
HybridVideoEngineInterface* engine,
|
|
|
|
VideoMediaChannel* channel1,
|
|
|
|
VideoMediaChannel* channel2)
|
|
|
|
: engine_(engine),
|
|
|
|
channel1_(channel1),
|
|
|
|
channel2_(channel2),
|
|
|
|
active_channel_(NULL),
|
|
|
|
sending_(false) {
|
|
|
|
}
|
|
|
|
|
|
|
|
HybridVideoMediaChannel::~HybridVideoMediaChannel() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void HybridVideoMediaChannel::SetInterface(NetworkInterface* iface) {
|
|
|
|
if (channel1_) {
|
|
|
|
channel1_->SetInterface(iface);
|
|
|
|
}
|
|
|
|
if (channel2_) {
|
|
|
|
channel2_->SetInterface(iface);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetOptions(const VideoOptions &options) {
|
|
|
|
bool ret = true;
|
|
|
|
if (channel1_) {
|
|
|
|
ret = channel1_->SetOptions(options);
|
|
|
|
}
|
|
|
|
if (channel2_ && ret) {
|
|
|
|
ret = channel2_->SetOptions(options);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::GetOptions(VideoOptions *options) const {
|
|
|
|
if (active_channel_) {
|
|
|
|
return active_channel_->GetOptions(options);
|
|
|
|
}
|
|
|
|
if (channel1_) {
|
|
|
|
return channel1_->GetOptions(options);
|
|
|
|
}
|
|
|
|
if (channel2_) {
|
|
|
|
return channel2_->GetOptions(options);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetRecvCodecs(
|
|
|
|
const std::vector<VideoCodec>& codecs) {
|
|
|
|
// Only give each channel the codecs it knows about.
|
|
|
|
bool ret = true;
|
|
|
|
std::vector<VideoCodec> codecs1, codecs2;
|
|
|
|
SplitCodecs(codecs, &codecs1, &codecs2);
|
|
|
|
if (channel1_) {
|
|
|
|
ret = channel1_->SetRecvCodecs(codecs1);
|
|
|
|
}
|
|
|
|
if (channel2_ && ret) {
|
|
|
|
ret = channel2_->SetRecvCodecs(codecs2);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetRecvRtpHeaderExtensions(
|
|
|
|
const std::vector<RtpHeaderExtension>& extensions) {
|
|
|
|
bool ret = true;
|
|
|
|
if (channel1_) {
|
|
|
|
ret = channel1_->SetRecvRtpHeaderExtensions(extensions);
|
|
|
|
}
|
|
|
|
if (channel2_ && ret) {
|
|
|
|
ret = channel2_->SetRecvRtpHeaderExtensions(extensions);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetRenderer(uint32 ssrc,
|
|
|
|
VideoRenderer* renderer) {
|
|
|
|
bool ret = true;
|
|
|
|
if (channel1_) {
|
|
|
|
ret = channel1_->SetRenderer(ssrc, renderer);
|
|
|
|
}
|
|
|
|
if (channel2_ && ret) {
|
|
|
|
ret = channel2_->SetRenderer(ssrc, renderer);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetRender(bool render) {
|
|
|
|
bool ret = true;
|
|
|
|
if (channel1_) {
|
|
|
|
ret = channel1_->SetRender(render);
|
|
|
|
}
|
|
|
|
if (channel2_ && ret) {
|
|
|
|
ret = channel2_->SetRender(render);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::MuteStream(uint32 ssrc, bool muted) {
|
|
|
|
bool ret = true;
|
|
|
|
if (channel1_) {
|
|
|
|
ret = channel1_->MuteStream(ssrc, muted);
|
|
|
|
}
|
|
|
|
if (channel2_ && ret) {
|
|
|
|
ret = channel2_->MuteStream(ssrc, muted);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetSendCodecs(
|
|
|
|
const std::vector<VideoCodec>& codecs) {
|
|
|
|
// Use the input to this function to decide what impl we're going to use.
|
|
|
|
if (!active_channel_ && !SelectActiveChannel(codecs)) {
|
|
|
|
LOG(LS_WARNING) << "Failed to select active channel";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Only give the active channel the codecs it knows about.
|
|
|
|
std::vector<VideoCodec> codecs1, codecs2;
|
|
|
|
SplitCodecs(codecs, &codecs1, &codecs2);
|
|
|
|
const std::vector<VideoCodec>& codecs_to_set =
|
|
|
|
(active_channel_ == channel1_.get()) ? codecs1 : codecs2;
|
|
|
|
bool return_value = active_channel_->SetSendCodecs(codecs_to_set);
|
|
|
|
if (!return_value) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
VideoCodec send_codec;
|
|
|
|
return_value = active_channel_->GetSendCodec(&send_codec);
|
|
|
|
if (!return_value) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
engine_->OnNewSendResolution(send_codec.width, send_codec.height);
|
|
|
|
active_channel_->UpdateAspectRatio(send_codec.width, send_codec.height);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::GetSendCodec(VideoCodec* send_codec) {
|
|
|
|
if (!active_channel_) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return active_channel_->GetSendCodec(send_codec);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetSendStreamFormat(uint32 ssrc,
|
|
|
|
const VideoFormat& format) {
|
|
|
|
return active_channel_ && active_channel_->SetSendStreamFormat(ssrc, format);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetSendRtpHeaderExtensions(
|
|
|
|
const std::vector<RtpHeaderExtension>& extensions) {
|
|
|
|
return active_channel_ &&
|
|
|
|
active_channel_->SetSendRtpHeaderExtensions(extensions);
|
|
|
|
}
|
|
|
|
|
2014-01-16 00:15:54 +01:00
|
|
|
bool HybridVideoMediaChannel::SetStartSendBandwidth(int bps) {
|
|
|
|
return active_channel_ && active_channel_->SetStartSendBandwidth(bps);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetMaxSendBandwidth(int bps) {
|
|
|
|
return active_channel_ && active_channel_->SetMaxSendBandwidth(bps);
|
2013-07-10 02:45:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetSend(bool send) {
|
|
|
|
if (send == sending()) {
|
|
|
|
return true; // no action required if already set.
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ret = active_channel_ &&
|
|
|
|
active_channel_->SetSend(send);
|
|
|
|
|
|
|
|
// Returns error and don't connect the signal if starting up.
|
|
|
|
// Disconnects the signal anyway if shutting down.
|
|
|
|
if (ret || !send) {
|
|
|
|
// TODO(juberti): Remove this hack that connects the WebRTC channel
|
|
|
|
// to the capturer.
|
|
|
|
if (active_channel_ == channel1_.get()) {
|
|
|
|
engine_->OnSendChange1(channel1_.get(), send);
|
|
|
|
} else {
|
|
|
|
engine_->OnSendChange2(channel2_.get(), send);
|
|
|
|
}
|
|
|
|
// If succeeded, remember the state as is.
|
|
|
|
// If failed to open, sending_ should be false.
|
|
|
|
// If failed to stop, sending_ should also be false, as we disconnect the
|
|
|
|
// capture anyway.
|
|
|
|
// The failure on SetSend(false) is a known issue in webrtc.
|
|
|
|
sending_ = send;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SetCapturer(uint32 ssrc,
|
|
|
|
VideoCapturer* capturer) {
|
|
|
|
bool ret = true;
|
|
|
|
if (channel1_.get()) {
|
|
|
|
ret = channel1_->SetCapturer(ssrc, capturer);
|
|
|
|
}
|
|
|
|
if (channel2_.get() && ret) {
|
|
|
|
ret = channel2_->SetCapturer(ssrc, capturer);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::AddSendStream(const StreamParams& sp) {
|
|
|
|
bool ret = true;
|
|
|
|
if (channel1_) {
|
|
|
|
ret = channel1_->AddSendStream(sp);
|
|
|
|
}
|
|
|
|
if (channel2_ && ret) {
|
|
|
|
ret = channel2_->AddSendStream(sp);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::RemoveSendStream(uint32 ssrc) {
|
|
|
|
bool ret = true;
|
|
|
|
if (channel1_) {
|
|
|
|
ret = channel1_->RemoveSendStream(ssrc);
|
|
|
|
}
|
|
|
|
if (channel2_ && ret) {
|
|
|
|
ret = channel2_->RemoveSendStream(ssrc);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::AddRecvStream(const StreamParams& sp) {
|
|
|
|
return active_channel_ &&
|
|
|
|
active_channel_->AddRecvStream(sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::RemoveRecvStream(uint32 ssrc) {
|
|
|
|
return active_channel_ &&
|
|
|
|
active_channel_->RemoveRecvStream(ssrc);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SendIntraFrame() {
|
|
|
|
return active_channel_ &&
|
|
|
|
active_channel_->SendIntraFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::RequestIntraFrame() {
|
|
|
|
return active_channel_ &&
|
|
|
|
active_channel_->RequestIntraFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::GetStats(VideoMediaInfo* info) {
|
|
|
|
// TODO(juberti): Ensure that returning no stats until SetSendCodecs is OK.
|
|
|
|
return active_channel_ &&
|
|
|
|
active_channel_->GetStats(info);
|
|
|
|
}
|
|
|
|
|
2013-12-13 01:21:03 +01:00
|
|
|
void HybridVideoMediaChannel::OnPacketReceived(
|
|
|
|
talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
|
2013-07-10 02:45:36 +02:00
|
|
|
// Eat packets until we have an active channel;
|
|
|
|
if (active_channel_) {
|
2013-12-13 01:21:03 +01:00
|
|
|
active_channel_->OnPacketReceived(packet, packet_time);
|
2013-07-10 02:45:36 +02:00
|
|
|
} else {
|
|
|
|
LOG(LS_INFO) << "HybridVideoChannel: Eating early RTP packet";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-13 01:21:03 +01:00
|
|
|
void HybridVideoMediaChannel::OnRtcpReceived(
|
|
|
|
talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
|
2013-07-10 02:45:36 +02:00
|
|
|
// Eat packets until we have an active channel;
|
|
|
|
if (active_channel_) {
|
2013-12-13 01:21:03 +01:00
|
|
|
active_channel_->OnRtcpReceived(packet, packet_time);
|
2013-07-10 02:45:36 +02:00
|
|
|
} else {
|
|
|
|
LOG(LS_INFO) << "HybridVideoChannel: Eating early RTCP packet";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HybridVideoMediaChannel::OnReadyToSend(bool ready) {
|
|
|
|
if (channel1_) {
|
|
|
|
channel1_->OnReadyToSend(ready);
|
|
|
|
}
|
|
|
|
if (channel2_) {
|
|
|
|
channel2_->OnReadyToSend(ready);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HybridVideoMediaChannel::UpdateAspectRatio(int ratio_w, int ratio_h) {
|
|
|
|
if (active_channel_) active_channel_->UpdateAspectRatio(ratio_w, ratio_h);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HybridVideoMediaChannel::SelectActiveChannel(
|
|
|
|
const std::vector<VideoCodec>& codecs) {
|
|
|
|
if (!active_channel_ && !codecs.empty()) {
|
|
|
|
if (engine_->HasCodec1(codecs[0])) {
|
|
|
|
channel2_.reset();
|
|
|
|
active_channel_ = channel1_.get();
|
|
|
|
} else if (engine_->HasCodec2(codecs[0])) {
|
|
|
|
channel1_.reset();
|
|
|
|
active_channel_ = channel2_.get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (NULL == active_channel_) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Connect signals from the active channel.
|
|
|
|
active_channel_->SignalMediaError.connect(
|
|
|
|
this,
|
|
|
|
&HybridVideoMediaChannel::OnMediaError);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HybridVideoMediaChannel::SplitCodecs(
|
|
|
|
const std::vector<VideoCodec>& codecs,
|
|
|
|
std::vector<VideoCodec>* codecs1, std::vector<VideoCodec>* codecs2) {
|
|
|
|
codecs1->clear();
|
|
|
|
codecs2->clear();
|
|
|
|
for (size_t i = 0; i < codecs.size(); ++i) {
|
|
|
|
if (engine_->HasCodec1(codecs[i])) {
|
|
|
|
codecs1->push_back(codecs[i]);
|
|
|
|
}
|
|
|
|
if (engine_->HasCodec2(codecs[i])) {
|
|
|
|
codecs2->push_back(codecs[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HybridVideoMediaChannel::OnMediaError(uint32 ssrc, Error error) {
|
|
|
|
SignalMediaError(ssrc, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace cricket
|