/* * Copyright 2022 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #ifndef MEDIA_BASE_MEDIA_CHANNEL_IMPL_H_ #define MEDIA_BASE_MEDIA_CHANNEL_IMPL_H_ #include #include #include #include #include #include #include #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/audio_options.h" #include "api/call/audio_sink.h" #include "api/call/transport.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/crypto/frame_encryptor_interface.h" #include "api/frame_transformer_interface.h" #include "api/media_types.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" #include "api/transport/rtp/rtp_source.h" #include "api/video/recordable_encoded_frame.h" #include "api/video/video_frame.h" #include "api/video/video_sink_interface.h" #include "api/video/video_source_interface.h" #include "api/video_codecs/video_encoder_factory.h" #include "media/base/codec.h" #include "media/base/media_channel.h" #include "media/base/stream_params.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/checks.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/dscp.h" #include "rtc_base/logging.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/network_route.h" #include "rtc_base/socket.h" #include "rtc_base/thread_annotations.h" // This file contains the base classes for classes that implement // the MediaChannel interfaces. // These implementation classes used to be the exposed interface names, // but this is in the process of being changed. // TODO(bugs.webrtc.org/13931): Consider removing these classes. // The target namespace cricket { class VoiceMediaChannel; class VideoMediaChannel; class MediaChannel : public MediaSendChannelInterface, public MediaReceiveChannelInterface { public: explicit MediaChannel(webrtc::TaskQueueBase* network_thread, bool enable_dscp = false); virtual ~MediaChannel(); // Downcasting to the subclasses. virtual VideoMediaChannel* AsVideoChannel() { RTC_CHECK_NOTREACHED(); return nullptr; } virtual VoiceMediaChannel* AsVoiceChannel() { RTC_CHECK_NOTREACHED(); return nullptr; } // Must declare the methods inherited from the base interface template, // even when abstract, to tell the compiler that all instances of the name // referred to by subclasses of this share the same implementation. cricket::MediaType media_type() const override = 0; void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override = 0; void OnPacketSent(const rtc::SentPacket& sent_packet) override = 0; void OnReadyToSend(bool ready) override = 0; void OnNetworkRouteChanged(absl::string_view transport_name, const rtc::NetworkRoute& network_route) override = 0; // Sets the abstract interface class for sending RTP/RTCP data. virtual void SetInterface(MediaChannelNetworkInterface* iface); // Returns the absolute sendtime extension id value from media channel. virtual int GetRtpSendTimeExtnId() const; // Base method to send packet using MediaChannelNetworkInterface. bool SendPacket(rtc::CopyOnWriteBuffer* packet, const rtc::PacketOptions& options); bool SendRtcp(rtc::CopyOnWriteBuffer* packet, const rtc::PacketOptions& options); int SetOption(MediaChannelNetworkInterface::SocketType type, rtc::Socket::Option opt, int option); // Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285. // Set to true if it's allowed to mix one- and two-byte RTP header extensions // in the same stream. The setter and getter must only be called from // worker_thread. void SetExtmapAllowMixed(bool extmap_allow_mixed) override; bool ExtmapAllowMixed() const override; // Returns `true` if a non-null MediaChannelNetworkInterface pointer is held. // Must be called on the network thread. bool HasNetworkInterface() const; void SetFrameEncryptor(uint32_t ssrc, rtc::scoped_refptr frame_encryptor) override; void SetFrameDecryptor(uint32_t ssrc, rtc::scoped_refptr frame_decryptor) override; void SetEncoderToPacketizerFrameTransformer( uint32_t ssrc, rtc::scoped_refptr frame_transformer) override; void SetDepacketizerToDecoderFrameTransformer( uint32_t ssrc, rtc::scoped_refptr frame_transformer) override; protected: int SetOptionLocked(MediaChannelNetworkInterface::SocketType type, rtc::Socket::Option opt, int option) RTC_RUN_ON(network_thread_); bool DscpEnabled() const; // This is the DSCP value used for both RTP and RTCP channels if DSCP is // enabled. It can be changed at any time via `SetPreferredDscp`. rtc::DiffServCodePoint PreferredDscp() const; void SetPreferredDscp(rtc::DiffServCodePoint new_dscp); rtc::scoped_refptr network_safety(); // Utility implementation for derived classes (video/voice) that applies // the packet options and passes the data onwards to `SendPacket`. void SendRtp(const uint8_t* data, size_t len, const webrtc::PacketOptions& options); void SendRtcp(const uint8_t* data, size_t len); private: // Apply the preferred DSCP setting to the underlying network interface RTP // and RTCP channels. If DSCP is disabled, then apply the default DSCP value. void UpdateDscp() RTC_RUN_ON(network_thread_); bool DoSendPacket(rtc::CopyOnWriteBuffer* packet, bool rtcp, const rtc::PacketOptions& options); const bool enable_dscp_; const rtc::scoped_refptr network_safety_ RTC_PT_GUARDED_BY(network_thread_); webrtc::TaskQueueBase* const network_thread_; MediaChannelNetworkInterface* network_interface_ RTC_GUARDED_BY(network_thread_) = nullptr; rtc::DiffServCodePoint preferred_dscp_ RTC_GUARDED_BY(network_thread_) = rtc::DSCP_DEFAULT; bool extmap_allow_mixed_ = false; }; // Base class for implementation classes class VideoMediaChannel : public MediaChannel, public VideoMediaSendChannelInterface, public VideoMediaReceiveChannelInterface { public: explicit VideoMediaChannel(webrtc::TaskQueueBase* network_thread, bool enable_dscp = false) : MediaChannel(network_thread, enable_dscp) {} ~VideoMediaChannel() override {} // Downcasting to the implemented interfaces. VideoMediaSendChannelInterface* AsVideoSendChannel() override { return this; } VoiceMediaSendChannelInterface* AsVoiceSendChannel() override { RTC_CHECK_NOTREACHED(); return nullptr; } VideoMediaReceiveChannelInterface* AsVideoReceiveChannel() override { return this; } cricket::MediaType media_type() const override; // Downcasting to the subclasses. VideoMediaChannel* AsVideoChannel() override { return this; } void SetExtmapAllowMixed(bool mixed) override { MediaChannel::SetExtmapAllowMixed(mixed); } bool ExtmapAllowMixed() const override { return MediaChannel::ExtmapAllowMixed(); } // This fills the "bitrate parts" (rtx, video bitrate) of the // BandwidthEstimationInfo, since that part that isn't possible to get // through webrtc::Call::GetStats, as they are statistics of the send // streams. // TODO(holmer): We should change this so that either BWE graphs doesn't // need access to bitrates of the streams, or change the (RTC)StatsCollector // so that it's getting the send stream stats separately by calling // GetStats(), and merges with BandwidthEstimationInfo by itself. void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) override = 0; // Gets quality stats for the channel. virtual bool GetSendStats(VideoMediaSendInfo* info) = 0; virtual bool GetReceiveStats(VideoMediaReceiveInfo* info) = 0; // Enable network condition based codec switching. void SetVideoCodecSwitchingEnabled(bool enabled) override; private: // Functions not implemented on this interface bool GetStats(VideoMediaSendInfo* info) override { RTC_CHECK_NOTREACHED(); return false; } bool GetStats(VideoMediaReceiveInfo* info) override { RTC_CHECK_NOTREACHED(); return false; } }; // Base class for implementation classes class VoiceMediaChannel : public MediaChannel, public VoiceMediaSendChannelInterface, public VoiceMediaReceiveChannelInterface { public: MediaType media_type() const override; VoiceMediaChannel(webrtc::TaskQueueBase* network_thread, bool enable_dscp = false) : MediaChannel(network_thread, enable_dscp) {} ~VoiceMediaChannel() override {} // Downcasting to the implemented interfaces. VoiceMediaSendChannelInterface* AsVoiceSendChannel() override { return this; } VoiceMediaReceiveChannelInterface* AsVoiceReceiveChannel() override { return this; } VoiceMediaChannel* AsVoiceChannel() override { return this; } VideoMediaSendChannelInterface* AsVideoSendChannel() override { RTC_CHECK_NOTREACHED(); return nullptr; } void SetExtmapAllowMixed(bool mixed) override { MediaChannel::SetExtmapAllowMixed(mixed); } bool ExtmapAllowMixed() const override { return MediaChannel::ExtmapAllowMixed(); } // Gets quality stats for the channel. virtual bool GetSendStats(VoiceMediaSendInfo* info) = 0; virtual bool GetReceiveStats(VoiceMediaReceiveInfo* info, bool get_and_clear_legacy_stats) = 0; private: // Functions not implemented on this interface bool GetStats(VoiceMediaSendInfo* info) override { RTC_CHECK_NOTREACHED(); return false; } bool GetStats(VoiceMediaReceiveInfo* info, bool get_and_clear_legacy_stats) override { RTC_CHECK_NOTREACHED(); return false; } }; // The externally exposed objects that support the Send and Receive interfaces. // These dispatch their functions to the underlying MediaChannel objects. class VoiceMediaSendChannel : public VoiceMediaSendChannelInterface { public: explicit VoiceMediaSendChannel(VoiceMediaChannel* impl) : impl_(impl) {} virtual ~VoiceMediaSendChannel() {} VoiceMediaSendChannelInterface* AsVoiceSendChannel() override { return this; } VideoMediaSendChannelInterface* AsVideoSendChannel() override { RTC_CHECK_NOTREACHED(); return nullptr; } // Implementation of MediaBaseChannelInterface cricket::MediaType media_type() const override { return MEDIA_TYPE_AUDIO; } void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override { impl()->OnPacketReceived(packet); } void OnPacketSent(const rtc::SentPacket& sent_packet) override { impl()->OnPacketSent(sent_packet); } void OnReadyToSend(bool ready) override { impl()->OnReadyToSend(ready); } void OnNetworkRouteChanged(absl::string_view transport_name, const rtc::NetworkRoute& network_route) override { impl()->OnNetworkRouteChanged(transport_name, network_route); } void SetExtmapAllowMixed(bool extmap_allow_mixed) override { impl()->SetExtmapAllowMixed(extmap_allow_mixed); } bool ExtmapAllowMixed() const override { return impl()->ExtmapAllowMixed(); } // Implementation of MediaSendChannelInterface bool AddSendStream(const StreamParams& sp) override { return impl()->AddSendStream(sp); } bool RemoveSendStream(uint32_t ssrc) override { return impl()->RemoveSendStream(ssrc); } void SetFrameEncryptor(uint32_t ssrc, rtc::scoped_refptr frame_encryptor) override { impl()->SetFrameEncryptor(ssrc, frame_encryptor); } webrtc::RTCError SetRtpSendParameters( uint32_t ssrc, const webrtc::RtpParameters& parameters, webrtc::SetParametersCallback callback = nullptr) override { return impl()->SetRtpSendParameters(ssrc, parameters, std::move(callback)); } void SetEncoderToPacketizerFrameTransformer( uint32_t ssrc, rtc::scoped_refptr frame_transformer) override { return impl()->SetEncoderToPacketizerFrameTransformer(ssrc, frame_transformer); } void SetEncoderSelector(uint32_t ssrc, webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) override { impl()->SetEncoderSelector(ssrc, encoder_selector); } webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const override { return impl()->GetRtpSendParameters(ssrc); } // Implementation of VoiceMediaSendChannel bool SetSendParameters(const AudioSendParameters& params) override { return impl()->SetSendParameters(params); } void SetSend(bool send) override { return impl()->SetSend(send); } bool SetAudioSend(uint32_t ssrc, bool enable, const AudioOptions* options, AudioSource* source) override { return impl()->SetAudioSend(ssrc, enable, options, source); } bool CanInsertDtmf() override { return impl()->CanInsertDtmf(); } bool InsertDtmf(uint32_t ssrc, int event, int duration) override { return impl()->InsertDtmf(ssrc, event, duration); } bool GetStats(VoiceMediaSendInfo* info) override { return impl_->GetSendStats(info); } private: VoiceMediaSendChannelInterface* impl() { return impl_; } const VoiceMediaSendChannelInterface* impl() const { return impl_; } VoiceMediaChannel* impl_; }; class VoiceMediaReceiveChannel : public VoiceMediaReceiveChannelInterface { public: explicit VoiceMediaReceiveChannel(VoiceMediaChannel* impl) : impl_(impl) {} virtual ~VoiceMediaReceiveChannel() {} // Implementation of MediaBaseChannelInterface cricket::MediaType media_type() const override { return MEDIA_TYPE_AUDIO; } void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override { impl()->OnPacketReceived(packet); } void OnPacketSent(const rtc::SentPacket& sent_packet) override { impl()->OnPacketSent(sent_packet); } void OnReadyToSend(bool ready) override { impl()->OnReadyToSend(ready); } void OnNetworkRouteChanged(absl::string_view transport_name, const rtc::NetworkRoute& network_route) override { impl()->OnNetworkRouteChanged(transport_name, network_route); } void SetExtmapAllowMixed(bool extmap_allow_mixed) override { impl()->SetExtmapAllowMixed(extmap_allow_mixed); } bool ExtmapAllowMixed() const override { return impl()->ExtmapAllowMixed(); } // Implementation of Delayable bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override { return impl()->SetBaseMinimumPlayoutDelayMs(ssrc, delay_ms); } absl::optional GetBaseMinimumPlayoutDelayMs( uint32_t ssrc) const override { return impl()->GetBaseMinimumPlayoutDelayMs(ssrc); } // Implementation of MediaReceiveChannelInterface bool AddRecvStream(const StreamParams& sp) override { return impl()->AddRecvStream(sp); } bool RemoveRecvStream(uint32_t ssrc) override { return impl()->RemoveRecvStream(ssrc); } void ResetUnsignaledRecvStream() override { return impl()->ResetUnsignaledRecvStream(); } absl::optional GetUnsignaledSsrc() const override { return impl()->GetUnsignaledSsrc(); } void OnDemuxerCriteriaUpdatePending() override { impl()->OnDemuxerCriteriaUpdatePending(); } void OnDemuxerCriteriaUpdateComplete() override { impl()->OnDemuxerCriteriaUpdateComplete(); } void SetFrameDecryptor(uint32_t ssrc, rtc::scoped_refptr frame_decryptor) override { impl()->SetFrameDecryptor(ssrc, frame_decryptor); } void SetDepacketizerToDecoderFrameTransformer( uint32_t ssrc, rtc::scoped_refptr frame_transformer) override { impl()->SetDepacketizerToDecoderFrameTransformer(ssrc, frame_transformer); } // Implementation of VoiceMediaReceiveChannelInterface bool SetRecvParameters(const AudioRecvParameters& params) override { return impl()->SetRecvParameters(params); } webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const override { return impl()->GetRtpReceiveParameters(ssrc); } std::vector GetSources(uint32_t ssrc) const override { return impl()->GetSources(ssrc); } webrtc::RtpParameters GetDefaultRtpReceiveParameters() const override { return impl()->GetDefaultRtpReceiveParameters(); } void SetPlayout(bool playout) override { return impl()->SetPlayout(playout); } bool SetOutputVolume(uint32_t ssrc, double volume) override { return impl()->SetOutputVolume(ssrc, volume); } bool SetDefaultOutputVolume(double volume) override { return impl()->SetDefaultOutputVolume(volume); } void SetRawAudioSink( uint32_t ssrc, std::unique_ptr sink) override { return impl()->SetRawAudioSink(ssrc, std::move(sink)); } void SetDefaultRawAudioSink( std::unique_ptr sink) override { return impl()->SetDefaultRawAudioSink(std::move(sink)); } bool GetStats(VoiceMediaReceiveInfo* info, bool reset_legacy) override { return impl_->GetReceiveStats(info, reset_legacy); } private: VoiceMediaReceiveChannelInterface* impl() { return impl_; } const VoiceMediaReceiveChannelInterface* impl() const { return impl_; } VoiceMediaChannel* impl_; }; class VideoMediaSendChannel : public VideoMediaSendChannelInterface { public: explicit VideoMediaSendChannel(VideoMediaChannel* impl) : impl_(impl) {} VideoMediaSendChannelInterface* AsVideoSendChannel() override { return this; } VoiceMediaSendChannelInterface* AsVoiceSendChannel() override { RTC_CHECK_NOTREACHED(); return nullptr; } // Implementation of MediaBaseChannelInterface cricket::MediaType media_type() const override { return MEDIA_TYPE_VIDEO; } void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override { impl()->OnPacketReceived(packet); } void OnPacketSent(const rtc::SentPacket& sent_packet) override { impl()->OnPacketSent(sent_packet); } void OnReadyToSend(bool ready) override { impl()->OnReadyToSend(ready); } void OnNetworkRouteChanged(absl::string_view transport_name, const rtc::NetworkRoute& network_route) override { impl()->OnNetworkRouteChanged(transport_name, network_route); } void SetExtmapAllowMixed(bool extmap_allow_mixed) override { impl()->SetExtmapAllowMixed(extmap_allow_mixed); } bool ExtmapAllowMixed() const override { return impl()->ExtmapAllowMixed(); } // Implementation of MediaSendChannelInterface bool AddSendStream(const StreamParams& sp) override { return impl()->AddSendStream(sp); } bool RemoveSendStream(uint32_t ssrc) override { return impl()->RemoveSendStream(ssrc); } void SetFrameEncryptor(uint32_t ssrc, rtc::scoped_refptr frame_encryptor) override { impl()->SetFrameEncryptor(ssrc, frame_encryptor); } webrtc::RTCError SetRtpSendParameters( uint32_t ssrc, const webrtc::RtpParameters& parameters, webrtc::SetParametersCallback callback = nullptr) override { return impl()->SetRtpSendParameters(ssrc, parameters, std::move(callback)); } void SetEncoderToPacketizerFrameTransformer( uint32_t ssrc, rtc::scoped_refptr frame_transformer) override { return impl()->SetEncoderToPacketizerFrameTransformer(ssrc, frame_transformer); } void SetEncoderSelector(uint32_t ssrc, webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) override { impl()->SetEncoderSelector(ssrc, encoder_selector); } webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const override { return impl()->GetRtpSendParameters(ssrc); } // Implementation of VideoMediaSendChannelInterface bool SetSendParameters(const VideoSendParameters& params) override { return impl()->SetSendParameters(params); } bool GetSendCodec(VideoCodec* send_codec) override { return impl()->GetSendCodec(send_codec); } bool SetSend(bool send) override { return impl()->SetSend(send); } bool SetVideoSend( uint32_t ssrc, const VideoOptions* options, rtc::VideoSourceInterface* source) override { return impl()->SetVideoSend(ssrc, options, source); } void GenerateSendKeyFrame(uint32_t ssrc, const std::vector& rids) override { return impl()->GenerateSendKeyFrame(ssrc, rids); } void SetVideoCodecSwitchingEnabled(bool enabled) override { return impl()->SetVideoCodecSwitchingEnabled(enabled); } bool GetStats(VideoMediaSendInfo* info) override { return impl_->GetSendStats(info); } void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) override { return impl_->FillBitrateInfo(bwe_info); } private: VideoMediaSendChannelInterface* impl() { return impl_; } const VideoMediaSendChannelInterface* impl() const { return impl_; } VideoMediaChannel* const impl_; }; class VideoMediaReceiveChannel : public VideoMediaReceiveChannelInterface { public: explicit VideoMediaReceiveChannel(VideoMediaChannel* impl) : impl_(impl) {} // Implementation of MediaBaseChannelInterface cricket::MediaType media_type() const override { return MEDIA_TYPE_VIDEO; } void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override { impl()->OnPacketReceived(packet); } void OnPacketSent(const rtc::SentPacket& sent_packet) override { impl()->OnPacketSent(sent_packet); } void OnReadyToSend(bool ready) override { impl()->OnReadyToSend(ready); } void OnNetworkRouteChanged(absl::string_view transport_name, const rtc::NetworkRoute& network_route) override { impl()->OnNetworkRouteChanged(transport_name, network_route); } void SetExtmapAllowMixed(bool extmap_allow_mixed) override { impl()->SetExtmapAllowMixed(extmap_allow_mixed); } bool ExtmapAllowMixed() const override { return impl()->ExtmapAllowMixed(); } // Implementation of Delayable bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override { return impl()->SetBaseMinimumPlayoutDelayMs(ssrc, delay_ms); } absl::optional GetBaseMinimumPlayoutDelayMs( uint32_t ssrc) const override { return impl()->GetBaseMinimumPlayoutDelayMs(ssrc); } // Implementation of MediaReceiveChannelInterface VideoMediaReceiveChannelInterface* AsVideoReceiveChannel() override { return this; } bool AddRecvStream(const StreamParams& sp) override { return impl()->AddRecvStream(sp); } bool RemoveRecvStream(uint32_t ssrc) override { return impl()->RemoveRecvStream(ssrc); } void ResetUnsignaledRecvStream() override { return impl()->ResetUnsignaledRecvStream(); } absl::optional GetUnsignaledSsrc() const override { return impl()->GetUnsignaledSsrc(); } void OnDemuxerCriteriaUpdatePending() override { impl()->OnDemuxerCriteriaUpdatePending(); } void OnDemuxerCriteriaUpdateComplete() override { impl()->OnDemuxerCriteriaUpdateComplete(); } void SetFrameDecryptor(uint32_t ssrc, rtc::scoped_refptr frame_decryptor) override { impl()->SetFrameDecryptor(ssrc, frame_decryptor); } void SetDepacketizerToDecoderFrameTransformer( uint32_t ssrc, rtc::scoped_refptr frame_transformer) override { impl()->SetDepacketizerToDecoderFrameTransformer(ssrc, frame_transformer); } // Implementation on videoMediaReceiveChannelInterface bool SetRecvParameters(const VideoRecvParameters& params) override { return impl()->SetRecvParameters(params); } webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const override { return impl()->GetRtpReceiveParameters(ssrc); } webrtc::RtpParameters GetDefaultRtpReceiveParameters() const override { return impl()->GetDefaultRtpReceiveParameters(); } bool SetSink(uint32_t ssrc, rtc::VideoSinkInterface* sink) override { return impl()->SetSink(ssrc, sink); } void SetDefaultSink( rtc::VideoSinkInterface* sink) override { return impl()->SetDefaultSink(sink); } void RequestRecvKeyFrame(uint32_t ssrc) override { return impl()->RequestRecvKeyFrame(ssrc); } std::vector GetSources(uint32_t ssrc) const override { return impl()->GetSources(ssrc); } // Set recordable encoded frame callback for `ssrc` void SetRecordableEncodedFrameCallback( uint32_t ssrc, std::function callback) override { return impl()->SetRecordableEncodedFrameCallback(ssrc, std::move(callback)); } // Clear recordable encoded frame callback for `ssrc` void ClearRecordableEncodedFrameCallback(uint32_t ssrc) override { impl()->ClearRecordableEncodedFrameCallback(ssrc); } bool GetStats(VideoMediaReceiveInfo* info) override { return impl_->GetReceiveStats(info); } private: VideoMediaReceiveChannelInterface* impl() { return impl_; } const VideoMediaReceiveChannelInterface* impl() const { return impl_; } VideoMediaChannel* const impl_; }; } // namespace cricket #endif // MEDIA_BASE_MEDIA_CHANNEL_IMPL_H_