1 /* 2 * libjingle 3 * Copyright 2004 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifndef TALK_MEDIA_BASE_HYBRIDVIDEOENGINE_H_ 29 #define TALK_MEDIA_BASE_HYBRIDVIDEOENGINE_H_ 30 31 #include <string> 32 #include <vector> 33 34 #include "talk/base/logging.h" 35 #include "talk/base/sigslotrepeater.h" 36 #include "talk/media/base/codec.h" 37 #include "talk/media/base/mediachannel.h" 38 #include "talk/media/base/videocapturer.h" 39 #include "talk/media/base/videocommon.h" 40 41 namespace cricket { 42 43 struct Device; 44 struct VideoFormat; 45 class HybridVideoEngineInterface; 46 class VideoCapturer; 47 class VideoFrame; 48 class VideoRenderer; 49 50 // HybridVideoMediaChannels work with a HybridVideoEngine to combine 51 // two unrelated VideoMediaChannel implementations into a single class. 52 class HybridVideoMediaChannel : public VideoMediaChannel { 53 public: 54 HybridVideoMediaChannel(HybridVideoEngineInterface* engine, 55 VideoMediaChannel* channel1, 56 VideoMediaChannel* channel2); 57 virtual ~HybridVideoMediaChannel(); 58 59 // VideoMediaChannel methods 60 virtual void SetInterface(NetworkInterface* iface); 61 virtual bool SetOptions(const VideoOptions& options); 62 virtual bool GetOptions(VideoOptions* options) const; 63 virtual bool AddSendStream(const StreamParams& sp); 64 virtual bool RemoveSendStream(uint32 ssrc); 65 virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer); 66 virtual bool SetRender(bool render); 67 virtual bool MuteStream(uint32 ssrc, bool muted); 68 69 virtual bool SetRecvCodecs(const std::vector<VideoCodec>& codecs); 70 virtual bool SetRecvRtpHeaderExtensions( 71 const std::vector<RtpHeaderExtension>& extensions); 72 73 virtual bool SetSendCodecs(const std::vector<VideoCodec>& codecs); 74 virtual bool GetSendCodec(VideoCodec* codec); 75 virtual bool SetSendStreamFormat(uint32 ssrc, const VideoFormat& format); 76 virtual bool SetSendRtpHeaderExtensions( 77 const std::vector<RtpHeaderExtension>& extensions); 78 virtual bool SetStartSendBandwidth(int bps); 79 virtual bool SetMaxSendBandwidth(int bps); 80 virtual bool SetSend(bool send); 81 82 virtual bool AddRecvStream(const StreamParams& sp); 83 virtual bool RemoveRecvStream(uint32 ssrc); 84 virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer); 85 86 virtual bool SendIntraFrame(); 87 virtual bool RequestIntraFrame(); 88 89 virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info); 90 91 virtual void OnPacketReceived(talk_base::Buffer* packet, 92 const talk_base::PacketTime& packet_time); 93 virtual void OnRtcpReceived(talk_base::Buffer* packet, 94 const talk_base::PacketTime& packet_time); 95 virtual void OnReadyToSend(bool ready); 96 97 virtual void UpdateAspectRatio(int ratio_w, int ratio_h); 98 99 void OnLocalFrame(VideoCapturer*, const VideoFrame*); 100 void OnLocalFrameFormat(VideoCapturer*, const VideoFormat*); 101 sending()102 bool sending() const { return sending_; } 103 104 private: 105 bool SelectActiveChannel(const std::vector<VideoCodec>& codecs); 106 void SplitCodecs(const std::vector<VideoCodec>& codecs, 107 std::vector<VideoCodec>* codecs1, 108 std::vector<VideoCodec>* codecs2); 109 110 void OnMediaError(uint32 ssrc, Error error); 111 112 HybridVideoEngineInterface* engine_; 113 talk_base::scoped_ptr<VideoMediaChannel> channel1_; 114 talk_base::scoped_ptr<VideoMediaChannel> channel2_; 115 VideoMediaChannel* active_channel_; 116 bool sending_; 117 }; 118 119 // Interface class for HybridVideoChannels to talk to the engine. 120 class HybridVideoEngineInterface { 121 public: ~HybridVideoEngineInterface()122 virtual ~HybridVideoEngineInterface() {} 123 virtual bool HasCodec1(const VideoCodec& codec) = 0; 124 virtual bool HasCodec2(const VideoCodec& codec) = 0; 125 virtual void OnSendChange1(VideoMediaChannel* channel1, bool send) = 0; 126 virtual void OnSendChange2(VideoMediaChannel* channel1, bool send) = 0; 127 virtual void OnNewSendResolution(int width, int height) = 0; 128 }; 129 130 // The HybridVideoEngine class combines two unrelated VideoEngine impls 131 // into a single class. It creates HybridVideoMediaChannels that also contain 132 // a VideoMediaChannel implementation from each engine. Policy is then used 133 // during call setup to determine which VideoMediaChannel should be used. 134 // Currently, this policy is based on what codec the remote side wants to use. 135 template<class VIDEO1, class VIDEO2> 136 class HybridVideoEngine : public HybridVideoEngineInterface { 137 public: HybridVideoEngine()138 HybridVideoEngine() { 139 // Unify the codec lists. 140 codecs_ = video1_.codecs(); 141 codecs_.insert(codecs_.end(), video2_.codecs().begin(), 142 video2_.codecs().end()); 143 144 rtp_header_extensions_ = video1_.rtp_header_extensions(); 145 rtp_header_extensions_.insert(rtp_header_extensions_.end(), 146 video2_.rtp_header_extensions().begin(), 147 video2_.rtp_header_extensions().end()); 148 149 SignalCaptureStateChange.repeat(video2_.SignalCaptureStateChange); 150 } 151 Init(talk_base::Thread * worker_thread)152 bool Init(talk_base::Thread* worker_thread) { 153 if (!video1_.Init(worker_thread)) { 154 LOG(LS_ERROR) << "Failed to init VideoEngine1"; 155 return false; 156 } 157 if (!video2_.Init(worker_thread)) { 158 LOG(LS_ERROR) << "Failed to init VideoEngine2"; 159 video1_.Terminate(); 160 return false; 161 } 162 return true; 163 } Terminate()164 void Terminate() { 165 video1_.Terminate(); 166 video2_.Terminate(); 167 } 168 GetCapabilities()169 int GetCapabilities() { 170 return (video1_.GetCapabilities() | video2_.GetCapabilities()); 171 } CreateChannel(VoiceMediaChannel * channel)172 HybridVideoMediaChannel* CreateChannel(VoiceMediaChannel* channel) { 173 talk_base::scoped_ptr<VideoMediaChannel> channel1( 174 video1_.CreateChannel(channel)); 175 if (!channel1) { 176 LOG(LS_ERROR) << "Failed to create VideoMediaChannel1"; 177 return NULL; 178 } 179 talk_base::scoped_ptr<VideoMediaChannel> channel2( 180 video2_.CreateChannel(channel)); 181 if (!channel2) { 182 LOG(LS_ERROR) << "Failed to create VideoMediaChannel2"; 183 return NULL; 184 } 185 return new HybridVideoMediaChannel(this, 186 channel1.release(), channel2.release()); 187 } 188 SetOptions(const VideoOptions & options)189 bool SetOptions(const VideoOptions& options) { 190 return video1_.SetOptions(options) && video2_.SetOptions(options); 191 } SetDefaultEncoderConfig(const VideoEncoderConfig & config)192 bool SetDefaultEncoderConfig(const VideoEncoderConfig& config) { 193 VideoEncoderConfig conf = config; 194 if (video1_.codecs().size() > 0) { 195 conf.max_codec.name = video1_.codecs()[0].name; 196 if (!video1_.SetDefaultEncoderConfig(conf)) { 197 LOG(LS_ERROR) << "Failed to SetDefaultEncoderConfig for video1"; 198 return false; 199 } 200 } 201 if (video2_.codecs().size() > 0) { 202 conf.max_codec.name = video2_.codecs()[0].name; 203 if (!video2_.SetDefaultEncoderConfig(conf)) { 204 LOG(LS_ERROR) << "Failed to SetDefaultEncoderConfig for video2"; 205 return false; 206 } 207 } 208 return true; 209 } GetDefaultEncoderConfig()210 VideoEncoderConfig GetDefaultEncoderConfig() const { 211 // This looks pretty strange, but, in practice, it'll do sane things if 212 // GetDefaultEncoderConfig is only called after SetDefaultEncoderConfig, 213 // since both engines should be essentially equivalent at that point. If it 214 // hasn't been called, though, we'll use the first meaningful encoder 215 // config, or the config from the second video engine if neither are 216 // meaningful. 217 VideoEncoderConfig config = video1_.GetDefaultEncoderConfig(); 218 if (config.max_codec.width != 0) { 219 return config; 220 } else { 221 return video2_.GetDefaultEncoderConfig(); 222 } 223 } codecs()224 const std::vector<VideoCodec>& codecs() const { 225 return codecs_; 226 } rtp_header_extensions()227 const std::vector<RtpHeaderExtension>& rtp_header_extensions() const { 228 return rtp_header_extensions_; 229 } SetLogging(int min_sev,const char * filter)230 void SetLogging(int min_sev, const char* filter) { 231 video1_.SetLogging(min_sev, filter); 232 video2_.SetLogging(min_sev, filter); 233 } 234 GetStartCaptureFormat()235 VideoFormat GetStartCaptureFormat() const { 236 return video2_.GetStartCaptureFormat(); 237 } 238 239 // TODO(juberti): Remove these functions after we do the capturer refactoring. 240 // For now they are set to always use the second engine for capturing, which 241 // is convenient given our intended use case. SetCaptureDevice(const Device * device)242 bool SetCaptureDevice(const Device* device) { 243 return video2_.SetCaptureDevice(device); 244 } GetVideoCapturer()245 VideoCapturer* GetVideoCapturer() const { 246 return video2_.GetVideoCapturer(); 247 } SetLocalRenderer(VideoRenderer * renderer)248 bool SetLocalRenderer(VideoRenderer* renderer) { 249 return video2_.SetLocalRenderer(renderer); 250 } 251 sigslot::repeater2<VideoCapturer*, CaptureState> SignalCaptureStateChange; 252 HasCodec1(const VideoCodec & codec)253 virtual bool HasCodec1(const VideoCodec& codec) { 254 return HasCodec(video1_, codec); 255 } HasCodec2(const VideoCodec & codec)256 virtual bool HasCodec2(const VideoCodec& codec) { 257 return HasCodec(video2_, codec); 258 } 259 template<typename VIDEO> HasCodec(const VIDEO & engine,const VideoCodec & codec)260 bool HasCodec(const VIDEO& engine, const VideoCodec& codec) const { 261 for (std::vector<VideoCodec>::const_iterator i = engine.codecs().begin(); 262 i != engine.codecs().end(); 263 ++i) { 264 if (i->Matches(codec)) { 265 return true; 266 } 267 } 268 return false; 269 } OnSendChange1(VideoMediaChannel * channel1,bool send)270 virtual void OnSendChange1(VideoMediaChannel* channel1, bool send) { 271 } OnSendChange2(VideoMediaChannel * channel2,bool send)272 virtual void OnSendChange2(VideoMediaChannel* channel2, bool send) { 273 } OnNewSendResolution(int width,int height)274 virtual void OnNewSendResolution(int width, int height) { 275 } 276 277 protected: 278 VIDEO1 video1_; 279 VIDEO2 video2_; 280 std::vector<VideoCodec> codecs_; 281 std::vector<RtpHeaderExtension> rtp_header_extensions_; 282 }; 283 284 } // namespace cricket 285 286 #endif // TALK_MEDIA_BASE_HYBRIDVIDEOENGINE_H_ 287