| /* |
| * 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. |
| */ |
| |
| #ifndef TALK_MEDIA_BASE_HYBRIDVIDEOENGINE_H_ |
| #define TALK_MEDIA_BASE_HYBRIDVIDEOENGINE_H_ |
| |
| #include <string> |
| #include <vector> |
| |
| #include "talk/base/logging.h" |
| #include "talk/base/sigslotrepeater.h" |
| #include "talk/media/base/codec.h" |
| #include "talk/media/base/mediachannel.h" |
| #include "talk/media/base/videocapturer.h" |
| #include "talk/media/base/videocommon.h" |
| |
| namespace cricket { |
| |
| struct Device; |
| struct VideoFormat; |
| class HybridVideoEngineInterface; |
| class VideoCapturer; |
| class VideoFrame; |
| class VideoRenderer; |
| |
| // HybridVideoMediaChannels work with a HybridVideoEngine to combine |
| // two unrelated VideoMediaChannel implementations into a single class. |
| class HybridVideoMediaChannel : public VideoMediaChannel { |
| public: |
| HybridVideoMediaChannel(HybridVideoEngineInterface* engine, |
| VideoMediaChannel* channel1, |
| VideoMediaChannel* channel2); |
| virtual ~HybridVideoMediaChannel(); |
| |
| // VideoMediaChannel methods |
| virtual void SetInterface(NetworkInterface* iface); |
| virtual bool SetOptions(const VideoOptions& options); |
| virtual bool GetOptions(VideoOptions* options) const; |
| virtual bool AddSendStream(const StreamParams& sp); |
| virtual bool RemoveSendStream(uint32 ssrc); |
| virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer); |
| virtual bool SetRender(bool render); |
| virtual bool MuteStream(uint32 ssrc, bool muted); |
| |
| virtual bool SetRecvCodecs(const std::vector<VideoCodec>& codecs); |
| virtual bool SetRecvRtpHeaderExtensions( |
| const std::vector<RtpHeaderExtension>& extensions); |
| |
| virtual bool SetSendCodecs(const std::vector<VideoCodec>& codecs); |
| virtual bool GetSendCodec(VideoCodec* codec); |
| virtual bool SetSendStreamFormat(uint32 ssrc, const VideoFormat& format); |
| virtual bool SetSendRtpHeaderExtensions( |
| const std::vector<RtpHeaderExtension>& extensions); |
| virtual bool SetSendBandwidth(bool autobw, int bps); |
| virtual bool SetSend(bool send); |
| |
| virtual bool AddRecvStream(const StreamParams& sp); |
| virtual bool RemoveRecvStream(uint32 ssrc); |
| virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer); |
| |
| virtual bool SendIntraFrame(); |
| virtual bool RequestIntraFrame(); |
| |
| virtual bool GetStats(VideoMediaInfo* info); |
| |
| virtual void OnPacketReceived(talk_base::Buffer* packet); |
| virtual void OnRtcpReceived(talk_base::Buffer* packet); |
| virtual void OnReadyToSend(bool ready); |
| |
| virtual void UpdateAspectRatio(int ratio_w, int ratio_h); |
| |
| void OnLocalFrame(VideoCapturer*, const VideoFrame*); |
| void OnLocalFrameFormat(VideoCapturer*, const VideoFormat*); |
| |
| bool sending() const { return sending_; } |
| |
| private: |
| bool SelectActiveChannel(const std::vector<VideoCodec>& codecs); |
| void SplitCodecs(const std::vector<VideoCodec>& codecs, |
| std::vector<VideoCodec>* codecs1, |
| std::vector<VideoCodec>* codecs2); |
| |
| void OnMediaError(uint32 ssrc, Error error); |
| |
| HybridVideoEngineInterface* engine_; |
| talk_base::scoped_ptr<VideoMediaChannel> channel1_; |
| talk_base::scoped_ptr<VideoMediaChannel> channel2_; |
| VideoMediaChannel* active_channel_; |
| bool sending_; |
| }; |
| |
| // Interface class for HybridVideoChannels to talk to the engine. |
| class HybridVideoEngineInterface { |
| public: |
| virtual ~HybridVideoEngineInterface() {} |
| virtual bool HasCodec1(const VideoCodec& codec) = 0; |
| virtual bool HasCodec2(const VideoCodec& codec) = 0; |
| virtual void OnSendChange1(VideoMediaChannel* channel1, bool send) = 0; |
| virtual void OnSendChange2(VideoMediaChannel* channel1, bool send) = 0; |
| virtual void OnNewSendResolution(int width, int height) = 0; |
| }; |
| |
| // The HybridVideoEngine class combines two unrelated VideoEngine impls |
| // into a single class. It creates HybridVideoMediaChannels that also contain |
| // a VideoMediaChannel implementation from each engine. Policy is then used |
| // during call setup to determine which VideoMediaChannel should be used. |
| // Currently, this policy is based on what codec the remote side wants to use. |
| template<class VIDEO1, class VIDEO2> |
| class HybridVideoEngine : public HybridVideoEngineInterface { |
| public: |
| HybridVideoEngine() { |
| // Unify the codec lists. |
| codecs_ = video1_.codecs(); |
| codecs_.insert(codecs_.end(), video2_.codecs().begin(), |
| video2_.codecs().end()); |
| |
| rtp_header_extensions_ = video1_.rtp_header_extensions(); |
| rtp_header_extensions_.insert(rtp_header_extensions_.end(), |
| video2_.rtp_header_extensions().begin(), |
| video2_.rtp_header_extensions().end()); |
| |
| SignalCaptureStateChange.repeat(video2_.SignalCaptureStateChange); |
| } |
| |
| bool Init(talk_base::Thread* worker_thread) { |
| if (!video1_.Init(worker_thread)) { |
| LOG(LS_ERROR) << "Failed to init VideoEngine1"; |
| return false; |
| } |
| if (!video2_.Init(worker_thread)) { |
| LOG(LS_ERROR) << "Failed to init VideoEngine2"; |
| video1_.Terminate(); |
| return false; |
| } |
| return true; |
| } |
| void Terminate() { |
| video1_.Terminate(); |
| video2_.Terminate(); |
| } |
| |
| int GetCapabilities() { |
| return (video1_.GetCapabilities() | video2_.GetCapabilities()); |
| } |
| HybridVideoMediaChannel* CreateChannel(VoiceMediaChannel* channel) { |
| talk_base::scoped_ptr<VideoMediaChannel> channel1( |
| video1_.CreateChannel(channel)); |
| if (!channel1) { |
| LOG(LS_ERROR) << "Failed to create VideoMediaChannel1"; |
| return NULL; |
| } |
| talk_base::scoped_ptr<VideoMediaChannel> channel2( |
| video2_.CreateChannel(channel)); |
| if (!channel2) { |
| LOG(LS_ERROR) << "Failed to create VideoMediaChannel2"; |
| return NULL; |
| } |
| return new HybridVideoMediaChannel(this, |
| channel1.release(), channel2.release()); |
| } |
| |
| bool SetOptions(const VideoOptions& options) { |
| return video1_.SetOptions(options) && video2_.SetOptions(options); |
| } |
| bool SetDefaultEncoderConfig(const VideoEncoderConfig& config) { |
| VideoEncoderConfig conf = config; |
| if (video1_.codecs().size() > 0) { |
| conf.max_codec.name = video1_.codecs()[0].name; |
| if (!video1_.SetDefaultEncoderConfig(conf)) { |
| LOG(LS_ERROR) << "Failed to SetDefaultEncoderConfig for video1"; |
| return false; |
| } |
| } |
| if (video2_.codecs().size() > 0) { |
| conf.max_codec.name = video2_.codecs()[0].name; |
| if (!video2_.SetDefaultEncoderConfig(conf)) { |
| LOG(LS_ERROR) << "Failed to SetDefaultEncoderConfig for video2"; |
| return false; |
| } |
| } |
| return true; |
| } |
| VideoEncoderConfig GetDefaultEncoderConfig() const { |
| // This looks pretty strange, but, in practice, it'll do sane things if |
| // GetDefaultEncoderConfig is only called after SetDefaultEncoderConfig, |
| // since both engines should be essentially equivalent at that point. If it |
| // hasn't been called, though, we'll use the first meaningful encoder |
| // config, or the config from the second video engine if neither are |
| // meaningful. |
| VideoEncoderConfig config = video1_.GetDefaultEncoderConfig(); |
| if (config.max_codec.width != 0) { |
| return config; |
| } else { |
| return video2_.GetDefaultEncoderConfig(); |
| } |
| } |
| const std::vector<VideoCodec>& codecs() const { |
| return codecs_; |
| } |
| const std::vector<RtpHeaderExtension>& rtp_header_extensions() const { |
| return rtp_header_extensions_; |
| } |
| void SetLogging(int min_sev, const char* filter) { |
| video1_.SetLogging(min_sev, filter); |
| video2_.SetLogging(min_sev, filter); |
| } |
| |
| VideoFormat GetStartCaptureFormat() const { |
| return video2_.GetStartCaptureFormat(); |
| } |
| |
| // TODO(juberti): Remove these functions after we do the capturer refactoring. |
| // For now they are set to always use the second engine for capturing, which |
| // is convenient given our intended use case. |
| bool SetCaptureDevice(const Device* device) { |
| return video2_.SetCaptureDevice(device); |
| } |
| VideoCapturer* GetVideoCapturer() const { |
| return video2_.GetVideoCapturer(); |
| } |
| bool SetLocalRenderer(VideoRenderer* renderer) { |
| return video2_.SetLocalRenderer(renderer); |
| } |
| sigslot::repeater2<VideoCapturer*, CaptureState> SignalCaptureStateChange; |
| |
| virtual bool HasCodec1(const VideoCodec& codec) { |
| return HasCodec(video1_, codec); |
| } |
| virtual bool HasCodec2(const VideoCodec& codec) { |
| return HasCodec(video2_, codec); |
| } |
| template<typename VIDEO> |
| bool HasCodec(const VIDEO& engine, const VideoCodec& codec) const { |
| for (std::vector<VideoCodec>::const_iterator i = engine.codecs().begin(); |
| i != engine.codecs().end(); |
| ++i) { |
| if (i->Matches(codec)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| virtual void OnSendChange1(VideoMediaChannel* channel1, bool send) { |
| } |
| virtual void OnSendChange2(VideoMediaChannel* channel2, bool send) { |
| } |
| virtual void OnNewSendResolution(int width, int height) { |
| } |
| |
| protected: |
| VIDEO1 video1_; |
| VIDEO2 video2_; |
| std::vector<VideoCodec> codecs_; |
| std::vector<RtpHeaderExtension> rtp_header_extensions_; |
| }; |
| |
| } // namespace cricket |
| |
| #endif // TALK_MEDIA_BASE_HYBRIDVIDEOENGINE_H_ |