/* * 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& codecs) { // Only give each channel the codecs it knows about. bool ret = true; std::vector 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& 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& 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 codecs1, codecs2; SplitCodecs(codecs, &codecs1, &codecs2); const std::vector& 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& extensions) { return active_channel_ && active_channel_->SetSendRtpHeaderExtensions(extensions); } bool HybridVideoMediaChannel::SetStartSendBandwidth(int bps) { return active_channel_ && active_channel_->SetStartSendBandwidth(bps); } bool HybridVideoMediaChannel::SetMaxSendBandwidth(int bps) { return active_channel_ && active_channel_->SetMaxSendBandwidth(bps); } 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( const StatsOptions& options, VideoMediaInfo* info) { // TODO(juberti): Ensure that returning no stats until SetSendCodecs is OK. return active_channel_ && active_channel_->GetStats(options, info); } void HybridVideoMediaChannel::OnPacketReceived( talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { // Eat packets until we have an active channel; if (active_channel_) { active_channel_->OnPacketReceived(packet, packet_time); } else { LOG(LS_INFO) << "HybridVideoChannel: Eating early RTP packet"; } } void HybridVideoMediaChannel::OnRtcpReceived( talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { // Eat packets until we have an active channel; if (active_channel_) { active_channel_->OnRtcpReceived(packet, packet_time); } 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& 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& codecs, std::vector* codecs1, std::vector* 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