diff --git a/webrtc/video_engine/internal/video_call.cc b/webrtc/video_engine/internal/video_call.cc new file mode 100644 index 0000000000..f5e44f6f12 --- /dev/null +++ b/webrtc/video_engine/internal/video_call.cc @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/internal/video_call.h" + +#include +#include +#include +#include + +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" +#include "webrtc/video_engine/include/vie_base.h" +#include "webrtc/video_engine/include/vie_codec.h" +#include "webrtc/video_engine/include/vie_rtp_rtcp.h" +#include "webrtc/video_engine/new_include/common.h" +#include "webrtc/video_engine/new_include/video_engine.h" +#include "webrtc/video_engine/internal/video_receive_stream.h" +#include "webrtc/video_engine/internal/video_send_stream.h" + +namespace webrtc { +namespace internal { + +VideoCall::VideoCall(webrtc::VideoEngine* video_engine, + newapi::Transport* send_transport) + : send_transport(send_transport), video_engine_(video_engine) { + assert(video_engine != NULL); + assert(send_transport != NULL); + + rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine_); + assert(rtp_rtcp_ != NULL); + + codec_ = ViECodec::GetInterface(video_engine_); + assert(codec_ != NULL); +} + +VideoCall::~VideoCall() { + rtp_rtcp_->Release(); + codec_->Release(); +} + +newapi::PacketReceiver* VideoCall::Receiver() { return this; } + +std::vector VideoCall::GetVideoCodecs() { + std::vector codecs; + + VideoCodec codec; + for (size_t i = 0; i < static_cast(codec_->NumberOfCodecs()); ++i) { + if (codec_->GetCodec(i, codec) == 0) { + codecs.push_back(codec); + } + } + return codecs; +} + +void VideoCall::GetDefaultSendConfig( + newapi::VideoSendStreamConfig* send_stream_config) { + *send_stream_config = newapi::VideoSendStreamConfig(); + codec_->GetCodec(0, send_stream_config->codec); +} + +newapi::VideoSendStream* VideoCall::CreateSendStream( + const newapi::VideoSendStreamConfig& send_stream_config) { + assert(send_stream_config.rtp.ssrcs.size() > 0); + assert(send_stream_config.codec.numberOfSimulcastStreams == 0 || + send_stream_config.codec.numberOfSimulcastStreams == + send_stream_config.rtp.ssrcs.size()); + VideoSendStream* send_stream = + new VideoSendStream(send_transport, video_engine_, send_stream_config); + for (size_t i = 0; i < send_stream_config.rtp.ssrcs.size(); ++i) { + uint32_t ssrc = send_stream_config.rtp.ssrcs[i]; + // SSRC must be previously unused! + assert(send_ssrcs_[ssrc] == NULL && + receive_ssrcs_.find(ssrc) == receive_ssrcs_.end()); + send_ssrcs_[ssrc] = send_stream; + } + return send_stream; +} + +newapi::SendStreamState* VideoCall::DestroySendStream( + newapi::VideoSendStream* send_stream) { + if (send_stream == NULL) { + return NULL; + } + // TODO(pbos): Remove it properly! Free the SSRCs! + delete static_cast(send_stream); + + // TODO(pbos): Return its previous state + return NULL; +} + +void VideoCall::GetDefaultReceiveConfig( + newapi::VideoReceiveStreamConfig* receive_stream_config) { + // TODO(pbos): This is not the default config. + *receive_stream_config = newapi::VideoReceiveStreamConfig(); +} + +newapi::VideoReceiveStream* VideoCall::CreateReceiveStream( + const newapi::VideoReceiveStreamConfig& receive_stream_config) { + assert(receive_ssrcs_[receive_stream_config.rtp.ssrc] == NULL); + + VideoReceiveStream* receive_stream = new VideoReceiveStream( + video_engine_, receive_stream_config, send_transport); + + receive_ssrcs_[receive_stream_config.rtp.ssrc] = receive_stream; + + return receive_stream; +} + +void VideoCall::DestroyReceiveStream( + newapi::VideoReceiveStream* receive_stream) { + if (receive_stream == NULL) { + return; + } + // TODO(pbos): Remove its SSRCs! + delete static_cast(receive_stream); +} + +uint32_t VideoCall::SendBitrateEstimate() { + // TODO(pbos): Return send-bitrate estimate + return 0; +} + +uint32_t VideoCall::ReceiveBitrateEstimate() { + // TODO(pbos): Return receive-bitrate estimate + return 0; +} + +bool VideoCall::DeliverRtcp(ModuleRTPUtility::RTPHeaderParser* rtp_parser, + const void* packet, size_t length) { + // TODO(pbos): Figure out what channel needs it actually. + // Do NOT broadcast! Also make sure it's a valid packet. + bool rtcp_delivered = false; + for (std::map::iterator it = + receive_ssrcs_.begin(); + it != receive_ssrcs_.end(); ++it) { + if (static_cast(it->second) + ->DeliverRtcp(packet, length)) { + rtcp_delivered = true; + } + } + return rtcp_delivered; +} + +bool VideoCall::DeliverRtp(ModuleRTPUtility::RTPHeaderParser* rtp_parser, + const void* packet, size_t length) { + WebRtcRTPHeader rtp_header; + + // TODO(pbos): ExtensionMap if there are extensions + if (!rtp_parser->Parse(rtp_header)) { + // TODO(pbos): Should this error be reported and trigger something? + return false; + } + + uint32_t ssrc = rtp_header.header.ssrc; + if (receive_ssrcs_.find(ssrc) == receive_ssrcs_.end()) { + // TODO(pbos): Log some warning, SSRC without receiver. + return false; + } + + VideoReceiveStream* receiver = + static_cast(receive_ssrcs_[ssrc]); + return receiver->DeliverRtp(packet, length); +} + +bool VideoCall::DeliverPacket(const void* packet, size_t length) { + // TODO(pbos): Respect the constness of packet. + ModuleRTPUtility::RTPHeaderParser rtp_parser( + const_cast(static_cast(packet)), length); + + if (rtp_parser.RTCP()) { + return DeliverRtcp(&rtp_parser, packet, length); + } + + return DeliverRtp(&rtp_parser, packet, length); +} + +} // namespace internal +} // namespace webrtc diff --git a/webrtc/video_engine/internal/video_call.h b/webrtc/video_engine/internal/video_call.h new file mode 100644 index 0000000000..fb7d266d0c --- /dev/null +++ b/webrtc/video_engine/internal/video_call.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_VIDEO_CALL_IMPL_H_ +#define WEBRTC_VIDEO_ENGINE_VIDEO_CALL_IMPL_H_ + +#include +#include + +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" +#include "webrtc/video_engine/new_include/common.h" +#include "webrtc/video_engine/new_include/video_engine.h" +#include "webrtc/video_engine/internal/video_receive_stream.h" +#include "webrtc/video_engine/internal/video_send_stream.h" + +namespace webrtc { + +class VideoEngine; +class ViERTP_RTCP; +class ViECodec; + +namespace internal { + +// TODO(pbos): Split out the packet receiver, should be sharable between +// VideoEngine and VoiceEngine. +class VideoCall : public newapi::VideoCall, public newapi::PacketReceiver { + public: + VideoCall(webrtc::VideoEngine* video_engine, + newapi::Transport* send_transport); + virtual ~VideoCall(); + + virtual newapi::PacketReceiver* Receiver() OVERRIDE; + virtual std::vector GetVideoCodecs() OVERRIDE; + + virtual void GetDefaultSendConfig( + newapi::VideoSendStreamConfig* send_stream_config) OVERRIDE; + + virtual newapi::VideoSendStream* CreateSendStream( + const newapi::VideoSendStreamConfig& send_stream_config) OVERRIDE; + + virtual newapi::SendStreamState* DestroySendStream( + newapi::VideoSendStream* send_stream) OVERRIDE; + + virtual void GetDefaultReceiveConfig( + newapi::VideoReceiveStreamConfig* receive_stream_config) OVERRIDE; + + virtual newapi::VideoReceiveStream* CreateReceiveStream( + const newapi::VideoReceiveStreamConfig& receive_stream_config) OVERRIDE; + + virtual void DestroyReceiveStream(newapi::VideoReceiveStream* receive_stream) + OVERRIDE; + + virtual uint32_t SendBitrateEstimate() OVERRIDE; + virtual uint32_t ReceiveBitrateEstimate() OVERRIDE; + + virtual bool DeliverPacket(const void* packet, size_t length) OVERRIDE; + + private: + bool DeliverRtp(ModuleRTPUtility::RTPHeaderParser* rtp_parser, + const void* packet, size_t length); + bool DeliverRtcp(ModuleRTPUtility::RTPHeaderParser* rtp_parser, + const void* packet, size_t length); + + newapi::Transport* send_transport; + + std::map receive_ssrcs_; + std::map send_ssrcs_; + + webrtc::VideoEngine* video_engine_; + ViERTP_RTCP* rtp_rtcp_; + ViECodec* codec_; + + DISALLOW_COPY_AND_ASSIGN(VideoCall); +}; +} // namespace internal +} // namespace webrtc + +#endif // WEBRTC_VIDEO_ENGINE_INTERNAL_VIDEO_CALL_H_ diff --git a/webrtc/video_engine/internal/video_engine.cc b/webrtc/video_engine/internal/video_engine.cc new file mode 100644 index 0000000000..4ba8c43e4b --- /dev/null +++ b/webrtc/video_engine/internal/video_engine.cc @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/new_include/video_engine.h" + +#include + +#include "webrtc/video_engine/include/vie_base.h" +#include "webrtc/video_engine/internal/video_call.h" +#include "webrtc/video_engine/vie_defines.h" + +namespace webrtc { +namespace internal { + +class VideoEngine : public newapi::VideoEngine { + public: + explicit VideoEngine(const newapi::VideoEngineConfig& engine_config) + : config_(engine_config) { + video_engine_ = webrtc::VideoEngine::Create(); + assert(video_engine_ != NULL); + + ViEBase* video_engine_base = ViEBase::GetInterface(video_engine_); + assert(video_engine_base != NULL); + if (video_engine_base->Init() != 0) { + abort(); + } + video_engine_base->Release(); + } + + virtual ~VideoEngine() { webrtc::VideoEngine::Delete(video_engine_); } + + virtual newapi::VideoCall* CreateCall(newapi::Transport* transport) OVERRIDE { + return new VideoCall(video_engine_, transport); + } + + private: + newapi::VideoEngineConfig config_; + webrtc::VideoEngine* video_engine_; + + DISALLOW_COPY_AND_ASSIGN(VideoEngine); +}; +} // internal + +namespace newapi { + +VideoEngine* VideoEngine::Create(const VideoEngineConfig& engine_config) { + return new internal::VideoEngine(engine_config); +} + +const char* Version() { return WEBRTC_SVNREVISION " (" BUILDINFO ")"; } +} // newapi +} // webrtc diff --git a/webrtc/video_engine/internal/video_receive_stream.cc b/webrtc/video_engine/internal/video_receive_stream.cc new file mode 100644 index 0000000000..805e23832a --- /dev/null +++ b/webrtc/video_engine/internal/video_receive_stream.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/internal/video_receive_stream.h" + +#include +#include + +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/video_engine/include/vie_base.h" +#include "webrtc/video_engine/include/vie_capture.h" +#include "webrtc/video_engine/include/vie_codec.h" +#include "webrtc/video_engine/include/vie_network.h" +#include "webrtc/video_engine/include/vie_render.h" +#include "webrtc/video_engine/include/vie_rtp_rtcp.h" +#include "webrtc/video_engine/new_include/video_receive_stream.h" + +namespace webrtc { +namespace internal { + +VideoReceiveStream::VideoReceiveStream( + webrtc::VideoEngine* video_engine, + const newapi::VideoReceiveStreamConfig& config, + newapi::Transport* transport) + : transport_(transport), config_(config) { + video_engine_base_ = ViEBase::GetInterface(video_engine); + // TODO(mflodman): Use the other CreateChannel method. + video_engine_base_->CreateChannel(channel_); + assert(channel_ != -1); + + rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine); + assert(rtp_rtcp_ != NULL); + + assert(config_.rtp.ssrc != 0); + + network_ = ViENetwork::GetInterface(video_engine); + assert(network_ != NULL); + + network_->RegisterSendTransport(channel_, *this); + + codec_ = ViECodec::GetInterface(video_engine); + + for (size_t i = 0; i < config_.codecs.size(); ++i) { + if (codec_->SetReceiveCodec(channel_, config_.codecs[i]) != 0) { + // TODO(pbos): Abort gracefully, this can be a runtime error. + // Factor out to an Init() method. + abort(); + } + } + + render_ = webrtc::ViERender::GetInterface(video_engine); + assert(render_ != NULL); + + if (render_->AddRenderer(channel_, kVideoI420, this) != 0) { + abort(); + } + + clock_ = Clock::GetRealTimeClock(); +} + +VideoReceiveStream::~VideoReceiveStream() { + network_->DeregisterSendTransport(channel_); + + video_engine_base_->Release(); + codec_->Release(); + network_->Release(); + render_->Release(); + rtp_rtcp_->Release(); +} + +void VideoReceiveStream::StartReceive() { + if (render_->StartRender(channel_)) { + abort(); + } + if (video_engine_base_->StartReceive(channel_) != 0) { + abort(); + } +} + +void VideoReceiveStream::StopReceive() { + if (render_->StopRender(channel_)) { + abort(); + } + if (video_engine_base_->StopReceive(channel_) != 0) { + abort(); + } +} + +void VideoReceiveStream::GetCurrentReceiveCodec(VideoCodec* receive_codec) { + // TODO(pbos): Implement +} + +void VideoReceiveStream::GetReceiveStatistics( + newapi::ReceiveStatistics* statistics) { + // TODO(pbos): Implement +} + +bool VideoReceiveStream::DeliverRtcp(const void* packet, size_t length) { + return network_->ReceivedRTCPPacket(channel_, packet, length) == 0; +} + +bool VideoReceiveStream::DeliverRtp(const void* packet, size_t length) { + return network_->ReceivedRTPPacket(channel_, packet, length) == 0; +} + +int VideoReceiveStream::FrameSizeChange(unsigned int width, unsigned int height, + unsigned int /*number_of_streams*/) { + width_ = width; + height_ = height; + return 0; +} + +int VideoReceiveStream::DeliverFrame(uint8_t* frame, int buffer_size, + uint32_t time_stamp, int64_t render_time) { + if (config_.renderer == NULL) { + return 0; + } + + I420VideoFrame video_frame; + video_frame.CreateEmptyFrame(width_, height_, width_, height_, height_); + ConvertToI420(kI420, frame, 0, 0, width_, height_, buffer_size, + webrtc::kRotateNone, &video_frame); + + if (config_.post_decode_callback != NULL) { + config_.post_decode_callback->FrameCallback(&video_frame); + } + + if (config_.renderer != NULL) { + // TODO(pbos): Add timing to RenderFrame call + config_.renderer + ->RenderFrame(video_frame, render_time - clock_->TimeInMilliseconds()); + } + + return 0; +} + +int VideoReceiveStream::SendPacket(int /*channel*/, const void* packet, + int length) { + assert(length >= 0); + return transport_->SendRTP(packet, static_cast(length)) ? 0 : -1; +} + +int VideoReceiveStream::SendRTCPPacket(int /*channel*/, const void* packet, + int length) { + assert(length >= 0); + return transport_->SendRTCP(packet, static_cast(length)) ? 0 : -1; +} +} // internal +} // webrtc diff --git a/webrtc/video_engine/internal/video_receive_stream.h b/webrtc/video_engine/internal/video_receive_stream.h new file mode 100644 index 0000000000..024a8b6c63 --- /dev/null +++ b/webrtc/video_engine/internal/video_receive_stream.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_VIDEO_RECEIVE_STREAM_IMPL_H_ +#define WEBRTC_VIDEO_ENGINE_VIDEO_RECEIVE_STREAM_IMPL_H_ + +#include + +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/video_engine/include/vie_render.h" +#include "webrtc/video_engine/new_include/video_receive_stream.h" + +namespace webrtc { + +class VideoEngine; +class ViEBase; +class ViECodec; +class ViENetwork; +class ViERender; +class ViERTP_RTCP; + +namespace internal { + +class VideoReceiveStream : public newapi::VideoReceiveStream, + public webrtc::ExternalRenderer, + public webrtc::Transport { + public: + VideoReceiveStream(webrtc::VideoEngine* video_engine, + const newapi::VideoReceiveStreamConfig& config, + newapi::Transport* transport); + virtual ~VideoReceiveStream(); + + virtual void StartReceive() OVERRIDE; + virtual void StopReceive() OVERRIDE; + + virtual void GetCurrentReceiveCodec(VideoCodec* receive_codec) OVERRIDE; + virtual void GetReceiveStatistics(newapi::ReceiveStatistics* statistics) + OVERRIDE; + + virtual bool DeliverRtcp(const void* packet, size_t length); + virtual bool DeliverRtp(const void* packet, size_t length); + + virtual int FrameSizeChange(unsigned int width, unsigned int height, + unsigned int /*number_of_streams*/) OVERRIDE; + + virtual int DeliverFrame(uint8_t* frame, int buffer_size, uint32_t time_stamp, + int64_t render_time) OVERRIDE; + + virtual int SendPacket(int /*channel*/, const void* packet, int length) + OVERRIDE; + + virtual int SendRTCPPacket(int /*channel*/, const void* packet, int length) + OVERRIDE; + + private: + newapi::Transport* transport_; + newapi::VideoReceiveStreamConfig config_; + Clock* clock_; + + ViEBase* video_engine_base_; + ViECodec* codec_; + ViENetwork* network_; + ViERender* render_; + ViERTP_RTCP* rtp_rtcp_; + + int channel_; + + // TODO(pbos): Remove VideoReceiveStream can operate on I420 frames directly. + unsigned int height_; + unsigned int width_; +}; +} // internal +} // webrtc + +#endif // WEBRTC_VIDEO_ENGINE_INTERNAL_VIDEO_RECEIVE_STREAM_H_ diff --git a/webrtc/video_engine/internal/video_send_stream.cc b/webrtc/video_engine/internal/video_send_stream.cc new file mode 100644 index 0000000000..c81825871c --- /dev/null +++ b/webrtc/video_engine/internal/video_send_stream.cc @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/internal/video_send_stream.h" + +#include + +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/video_engine/include/vie_base.h" +#include "webrtc/video_engine/include/vie_capture.h" +#include "webrtc/video_engine/include/vie_codec.h" +#include "webrtc/video_engine/include/vie_network.h" +#include "webrtc/video_engine/include/vie_rtp_rtcp.h" +#include "webrtc/video_engine/new_include/video_send_stream.h" + +namespace webrtc { +namespace internal { + +VideoSendStream::VideoSendStream( + newapi::Transport* transport, webrtc::VideoEngine* video_engine, + const newapi::VideoSendStreamConfig& send_stream_config) + : transport_(transport), config_(send_stream_config) { + + if (config_.codec.numberOfSimulcastStreams > 0) { + assert(config_.rtp.ssrcs.size() == config_.codec.numberOfSimulcastStreams); + } else { + assert(config_.rtp.ssrcs.size() == 1); + } + + video_engine_base_ = ViEBase::GetInterface(video_engine); + video_engine_base_->CreateChannel(channel_); + assert(channel_ != -1); + + rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine); + assert(rtp_rtcp_ != NULL); + + assert(config_.rtp.ssrcs.size() == 1); + rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.ssrcs[0]); + + capture_ = ViECapture::GetInterface(video_engine); + capture_->AllocateExternalCaptureDevice(capture_id_, external_capture_); + capture_->ConnectCaptureDevice(capture_id_, channel_); + + network_ = ViENetwork::GetInterface(video_engine); + assert(network_ != NULL); + + network_->RegisterSendTransport(channel_, *this); + + codec_ = ViECodec::GetInterface(video_engine); + if (codec_->SetSendCodec(channel_, config_.codec) != 0) { + abort(); + } +} + +VideoSendStream::~VideoSendStream() { + network_->DeregisterSendTransport(channel_); + video_engine_base_->DeleteChannel(channel_); + + capture_->DisconnectCaptureDevice(channel_); + capture_->ReleaseCaptureDevice(capture_id_); + + video_engine_base_->Release(); + capture_->Release(); + codec_->Release(); + network_->Release(); + rtp_rtcp_->Release(); +} + +void VideoSendStream::PutFrame(const I420VideoFrame& frame, + int32_t delta_capture_time) { + I420VideoFrame frame_copy; + frame_copy.CopyFrame(frame); + + if (config_.pre_encode_callback != NULL) { + config_.pre_encode_callback->FrameCallback(&frame_copy); + } + + ViEVideoFrameI420 vf; + + // TODO(pbos): This represents a memcpy step and is only required because + // external_capture_ only takes ViEVideoFrameI420s. + vf.y_plane = frame_copy.buffer(kYPlane); + vf.u_plane = frame_copy.buffer(kUPlane); + vf.v_plane = frame_copy.buffer(kVPlane); + vf.y_pitch = frame.stride(kYPlane); + vf.u_pitch = frame.stride(kUPlane); + vf.v_pitch = frame.stride(kVPlane); + vf.width = frame.width(); + vf.height = frame.height(); + + external_capture_->IncomingFrameI420(vf, frame.timestamp()); + + if (config_.local_renderer != NULL) { + config_.local_renderer->RenderFrame(frame, 0); + } +} + +newapi::VideoSendStreamInput* VideoSendStream::Input() { return this; } + +void VideoSendStream::StartSend() { + if (video_engine_base_->StartSend(channel_) != 0) abort(); +} + +void VideoSendStream::StopSend() { + if (video_engine_base_->StopSend(channel_) != 0) abort(); +} + +void VideoSendStream::GetSendStatistics( + std::vector* statistics) { + // TODO(pbos): Implement +} + +bool VideoSendStream::SetTargetBitrate( + int min_bitrate, int max_bitrate, + const std::vector& streams) { + return false; +} + +void VideoSendStream::GetSendCodec(VideoCodec* send_codec) { + *send_codec = config_.codec; +} + +int VideoSendStream::SendPacket(int /*channel*/, const void* packet, + int length) { + // TODO(pbos): Lock these methods and the destructor so it can't be processing + // a packet when the destructor has been called. + assert(length >= 0); + return transport_->SendRTP(packet, static_cast(length)) ? 0 : -1; +} + +int VideoSendStream::SendRTCPPacket(int /*channel*/, const void* packet, + int length) { + assert(length >= 0); + return transport_->SendRTCP(packet, static_cast(length)) ? 0 : -1; +} + +} // namespace internal +} // namespace webrtc diff --git a/webrtc/video_engine/internal/video_send_stream.h b/webrtc/video_engine/internal/video_send_stream.h new file mode 100644 index 0000000000..b1780332b1 --- /dev/null +++ b/webrtc/video_engine/internal/video_send_stream.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_VIDEO_SEND_STREAM_IMPL_H_ +#define WEBRTC_VIDEO_ENGINE_VIDEO_SEND_STREAM_IMPL_H_ + +#include + +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/video_engine/new_include/video_send_stream.h" +#include "webrtc/video_engine/new_include/video_receive_stream.h" + +namespace webrtc { + +class VideoEngine; +class ViEBase; +class ViECapture; +class ViECodec; +class ViEExternalCapture; +class ViENetwork; +class ViERTP_RTCP; + +namespace internal { +class VideoSendStream : public newapi::VideoSendStream, + public newapi::VideoSendStreamInput, + public webrtc::Transport { + public: + VideoSendStream(newapi::Transport* transport, + webrtc::VideoEngine* video_engine, + const newapi::VideoSendStreamConfig& send_stream_config); + + virtual ~VideoSendStream(); + + virtual void PutFrame(const I420VideoFrame& frame, int32_t delta_capture_time) + OVERRIDE; + + virtual newapi::VideoSendStreamInput* Input() OVERRIDE; + + virtual void StartSend() OVERRIDE; + + virtual void StopSend() OVERRIDE; + + virtual void GetSendStatistics( + std::vector* statistics) OVERRIDE; + + virtual bool SetTargetBitrate(int min_bitrate, int max_bitrate, + const std::vector& streams) + OVERRIDE; + + virtual void GetSendCodec(VideoCodec* send_codec) OVERRIDE; + + virtual int SendPacket(int /*channel*/, const void* packet, int length) + OVERRIDE; + + virtual int SendRTCPPacket(int /*channel*/, const void* packet, int length) + OVERRIDE; + + private: + newapi::Transport* transport_; + newapi::VideoSendStreamConfig config_; + + ViEBase* video_engine_base_; + ViECapture* capture_; + ViECodec* codec_; + ViEExternalCapture* external_capture_; + ViENetwork* network_; + ViERTP_RTCP* rtp_rtcp_; + + int channel_; + int capture_id_; +}; +} // namespace internal +} // namespace webrtc + +#endif // WEBRTC_VIDEO_ENGINE_INTERNAL_VIDEO_SEND_STREAM_H_ diff --git a/webrtc/video_engine/new_include/video_engine.h b/webrtc/video_engine/new_include/video_engine.h index 40c1815803..60a0a89110 100644 --- a/webrtc/video_engine/new_include/video_engine.h +++ b/webrtc/video_engine/new_include/video_engine.h @@ -19,7 +19,6 @@ #include "webrtc/video_engine/new_include/video_receive_stream.h" #include "webrtc/video_engine/new_include/video_send_stream.h" - namespace webrtc { namespace newapi { @@ -51,7 +50,7 @@ struct VideoEngineConfig { // estimates etc. class VideoCall { public: - virtual void GetVideoCodecs(std::vector* codecs) = 0; + virtual std::vector GetVideoCodecs() = 0; virtual void GetDefaultSendConfig(VideoSendStreamConfig* config) = 0; @@ -82,7 +81,6 @@ class VideoCall { // differ from the actual receive bitrate. virtual uint32_t ReceiveBitrateEstimate() = 0; - protected: virtual ~VideoCall() {} }; @@ -91,11 +89,9 @@ class VideoCall { class VideoEngine { public: static VideoEngine* Create(const VideoEngineConfig& engine_config); + virtual ~VideoEngine() {} virtual VideoCall* CreateCall(Transport* send_transport) = 0; - - protected: - virtual ~VideoEngine() {} }; } // namespace newapi diff --git a/webrtc/video_engine/test/common/direct_transport.h b/webrtc/video_engine/test/common/direct_transport.h new file mode 100644 index 0000000000..69de83ef9d --- /dev/null +++ b/webrtc/video_engine/test/common/direct_transport.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_TEST_COMMON_DIRECT_TRANSPORT_H_ +#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_DIRECT_TRANSPORT_H_ + +#include "webrtc/video_engine/new_include/common.h" + +namespace webrtc { +namespace test { + +class DirectTransport : public newapi::Transport { + public: + explicit DirectTransport(newapi::PacketReceiver* receiver) + : receiver_(receiver) {} + + void SetReceiver(newapi::PacketReceiver* receiver) { receiver_ = receiver; } + + bool SendRTP(const void* data, size_t length) OVERRIDE { + return receiver_->DeliverPacket(data, length); + } + + bool SendRTCP(const void* data, size_t length) OVERRIDE { + return SendRTP(data, length); + } + + private: + newapi::PacketReceiver* receiver_; +}; +} // test +} // webrtc + +#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_DIRECT_TRANSPORT_H_ diff --git a/webrtc/video_engine/test/common/generate_ssrcs.h b/webrtc/video_engine/test/common/generate_ssrcs.h new file mode 100644 index 0000000000..db6c6608ad --- /dev/null +++ b/webrtc/video_engine/test/common/generate_ssrcs.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_TEST_COMMON_GENERATE_SSRCS_H_ +#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_GENERATE_SSRCS_H_ + +#include +#include + +#include "webrtc/video_engine/new_include/video_send_stream.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +void GenerateRandomSsrcs(newapi::VideoSendStreamConfig* config, + std::map* reserved_ssrcs) { + size_t num_ssrcs = config->codec.numberOfSimulcastStreams; + std::vector* ssrcs = &config->rtp.ssrcs; + assert(ssrcs->size() == 0); + + if (num_ssrcs == 0) { + num_ssrcs = 1; + } + + while (ssrcs->size() < num_ssrcs) { + uint32_t rand_ssrc = static_cast(rand() + 1); // NOLINT + + // Make sure the ssrc is unique per-call + while (true) { + if (!(*reserved_ssrcs)[rand_ssrc]) { + (*reserved_ssrcs)[rand_ssrc] = true; + break; + } + ++rand_ssrc; + } + + ssrcs->push_back(rand_ssrc); + } +} +} // test +} // webrtc + +#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_GENERATE_SSRCS_H_ diff --git a/webrtc/video_engine/test/common/gl/gl_renderer.cc b/webrtc/video_engine/test/common/gl/gl_renderer.cc new file mode 100644 index 0000000000..3345fac3d5 --- /dev/null +++ b/webrtc/video_engine/test/common/gl/gl_renderer.cc @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/test/common/gl/gl_renderer.h" + +#include + +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" + +namespace webrtc { +namespace test { + +GlRenderer::GlRenderer() + : is_init_(false), buffer_(NULL), width_(0), height_(0) {} + +void GlRenderer::Init() { + assert(!is_init_); + is_init_ = true; + + glGenTextures(1, &texture_); +} + +void GlRenderer::Destroy() { + if (!is_init_) { + return; + } + + is_init_ = false; + + delete[] buffer_; + buffer_ = NULL; + + glDeleteTextures(1, &texture_); +} + +void GlRenderer::ResizeViewport(size_t width, size_t height) { + // TODO(pbos): Aspect ratio, letterbox the video. + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glOrtho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); +} + +void GlRenderer::ResizeVideo(size_t width, size_t height) { + assert(is_init_); + width_ = width; + height_ = height; + + buffer_size_ = width * height * 4; // BGRA + + delete[] buffer_; + buffer_ = new uint8_t[buffer_size_]; + assert(buffer_ != NULL); + memset(buffer_, 0, buffer_size_); + glBindTexture(GL_TEXTURE_2D, texture_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8, static_cast(buffer_)); +} + +void GlRenderer::RenderFrame(const webrtc::I420VideoFrame& frame, + int /*render_delay_ms*/) { + assert(is_init_); + + if (static_cast(frame.width()) != width_ || + static_cast(frame.height()) != height_) { + ResizeVideo(frame.width(), frame.height()); + } + + webrtc::ConvertFromI420(frame, kBGRA, 0, buffer_); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture_); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8, buffer_); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glLoadIdentity(); + + glBegin(GL_QUADS); + { + glTexCoord2f(0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + + glTexCoord2f(0.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + + glTexCoord2f(1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 0.0f); + + glTexCoord2f(1.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + } + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glFlush(); +} +} // test +} // webrtc diff --git a/webrtc/video_engine/test/common/gl/gl_renderer.h b/webrtc/video_engine/test/common/gl/gl_renderer.h new file mode 100644 index 0000000000..6a817ee33d --- /dev/null +++ b/webrtc/video_engine/test/common/gl/gl_renderer.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_TEST_COMMON_GL_GL_RENDERER_H_ +#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_GL_GL_RENDERER_H_ + +#include + +#include "webrtc/video_engine/test/common/video_renderer.h" + +namespace webrtc { +namespace test { + +class GlRenderer : public VideoRenderer { + public: + virtual void RenderFrame(const webrtc::I420VideoFrame& frame, + int time_to_render_ms) OVERRIDE; + + protected: + GlRenderer(); + + void Init(); + void Destroy(); + + void ResizeViewport(size_t width, size_t height); + + private: + bool is_init_; + uint8_t* buffer_; + GLuint texture_; + size_t width_, height_, buffer_size_; + + void ResizeVideo(size_t width, size_t height); +}; +} // test +} // webrtc + +#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_GL_GL_RENDERER_H_ diff --git a/webrtc/video_engine/test/common/linux/glx_renderer.cc b/webrtc/video_engine/test/common/linux/glx_renderer.cc new file mode 100644 index 0000000000..4f7c3d27d8 --- /dev/null +++ b/webrtc/video_engine/test/common/linux/glx_renderer.cc @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/test/common/linux/glx_renderer.h" + +#include +#include + +#include + +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" + +namespace webrtc { +namespace test { + +GlxRenderer::GlxRenderer(size_t width, size_t height) + : is_init_(false), + width_(width), + height_(height), + display_(NULL), + context_(NULL) { + assert(width > 0); + assert(height > 0); +} + +bool GlxRenderer::Init(const char* window_title) { + if ((display_ = XOpenDisplay(NULL)) == NULL) { + Destroy(); + return false; + } + + int screen = DefaultScreen(display_); + + XVisualInfo* vi; + int attr_list[] = { GLX_DOUBLEBUFFER, GLX_RGBA, GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, GLX_DEPTH_SIZE, 16, + None, }; + + if ((vi = glXChooseVisual(display_, screen, attr_list)) == NULL) { + Destroy(); + return false; + } + + context_ = glXCreateContext(display_, vi, 0, true); + if (context_ == NULL) { + Destroy(); + return false; + } + + XSetWindowAttributes window_attributes; + window_attributes.colormap = XCreateColormap( + display_, RootWindow(display_, vi->screen), vi->visual, AllocNone); + window_attributes.border_pixel = 0; + window_attributes.event_mask = StructureNotifyMask | ExposureMask; + window_ = XCreateWindow(display_, RootWindow(display_, vi->screen), 0, 0, + width_, height_, 0, vi->depth, InputOutput, + vi->visual, CWBorderPixel | CWColormap | CWEventMask, + &window_attributes); + XFree(vi); + + XSetStandardProperties(display_, window_, window_title, window_title, None, + NULL, 0, NULL); + + Atom wm_delete = XInternAtom(display_, "WM_DELETE_WINDOW", True); + if (wm_delete != None) { + XSetWMProtocols(display_, window_, &wm_delete, 1); + } + + XMapRaised(display_, window_); + + if (!glXMakeCurrent(display_, window_, context_)) { + Destroy(); + return false; + } + GlRenderer::Init(); + if (!glXMakeCurrent(display_, None, NULL)) { + Destroy(); + return false; + } + + Resize(width_, height_); + return true; +} + +void GlxRenderer::Destroy() { + if (!is_init_) { + return; + } + + is_init_ = false; + + if (context_ != NULL) { + glXMakeCurrent(display_, window_, context_); + GlRenderer::Destroy(); + glXMakeCurrent(display_, None, NULL); + glXDestroyContext(display_, context_); + context_ = NULL; + } + + if (display_ != NULL) { + XCloseDisplay(display_); + display_ = NULL; + } +} + +GlxRenderer* GlxRenderer::Create(const char* window_title, size_t width, + size_t height) { + GlxRenderer* glx_renderer = new GlxRenderer(width, height); + if (!glx_renderer->Init(window_title)) { + // TODO(pbos): Add GLX-failed warning here? + delete glx_renderer; + return NULL; + } + return glx_renderer; +} + +GlxRenderer::~GlxRenderer() { Destroy(); } + +void GlxRenderer::Resize(size_t width, size_t height) { + width_ = width; + height_ = height; + if (!glXMakeCurrent(display_, window_, context_)) { + abort(); + } + GlRenderer::ResizeViewport(width_, height_); + if (!glXMakeCurrent(display_, None, NULL)) { + abort(); + } + + XSizeHints* size_hints = XAllocSizeHints(); + if (size_hints == NULL) { + abort(); + } + size_hints->flags = PAspect; + size_hints->min_aspect.x = size_hints->max_aspect.x = width_; + size_hints->min_aspect.y = size_hints->max_aspect.y = height_; + XSetWMNormalHints(display_, window_, size_hints); + XFree(size_hints); +} + +void GlxRenderer::RenderFrame(const webrtc::I420VideoFrame& frame, + int /*render_delay_ms*/) { + if (static_cast(frame.width()) != width_ || + static_cast(frame.height()) != height_) { + Resize(static_cast(frame.width()), + static_cast(frame.height())); + } + + XEvent event; + if (!glXMakeCurrent(display_, window_, context_)) { + abort(); + } + while (XPending(display_)) { + XNextEvent(display_, &event); + switch (event.type) { + case ConfigureNotify: + GlRenderer::ResizeViewport(event.xconfigure.width, + event.xconfigure.height); + break; + default: + break; + } + } + + GlRenderer::RenderFrame(frame, 0); + glXSwapBuffers(display_, window_); + + if (!glXMakeCurrent(display_, None, NULL)) { + abort(); + } +} +} // test +} // webrtc diff --git a/webrtc/video_engine/test/common/linux/glx_renderer.h b/webrtc/video_engine/test/common/linux/glx_renderer.h new file mode 100644 index 0000000000..30fc8df2ce --- /dev/null +++ b/webrtc/video_engine/test/common/linux/glx_renderer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_GLX_RENDERER_H_ +#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_GLX_RENDERER_H_ + +#include +#include + +#include "webrtc/video_engine/test/common/gl/gl_renderer.h" + +namespace webrtc { +namespace test { + +class GlxRenderer : public GlRenderer { + public: + static GlxRenderer* Create(const char* window_title, size_t width, + size_t height); + virtual ~GlxRenderer(); + + virtual void RenderFrame(const webrtc::I420VideoFrame& frame, int delta) + OVERRIDE; + private: + GlxRenderer(size_t width, size_t height); + + bool Init(const char* window_title); + void Resize(size_t width, size_t height); + void Destroy(); + + bool is_init_; + size_t width_, height_; + + Display* display_; + Window window_; + GLXContext context_; +}; +} // test +} // webrtc + +#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_GLX_RENDERER_H_ diff --git a/webrtc/video_engine/test/common/linux/xv_renderer.cc b/webrtc/video_engine/test/common/linux/xv_renderer.cc new file mode 100644 index 0000000000..ba0b437332 --- /dev/null +++ b/webrtc/video_engine/test/common/linux/xv_renderer.cc @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/test/common/linux/xv_renderer.h" + +#include +#include +#include + +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" + +#define GUID_I420_PLANAR 0x30323449 + +namespace webrtc { +namespace test { + +XvRenderer::XvRenderer(size_t width, size_t height) + : width(width), + height(height), + is_init(false), + display(NULL), + gc(NULL), + image(NULL) { + assert(width > 0); + assert(height > 0); +} + +bool XvRenderer::Init(const char* window_title) { + assert(!is_init); + is_init = true; + if ((display = XOpenDisplay(NULL)) == NULL) { + Destroy(); + return false; + } + + int screen = DefaultScreen(display); + + XVisualInfo vinfo; + if (!XMatchVisualInfo(display, screen, 24, TrueColor, &vinfo)) { + Destroy(); + return false; + } + + XSetWindowAttributes xswa; + xswa.colormap = XCreateColormap(display, DefaultRootWindow(display), + vinfo.visual, AllocNone); + xswa.event_mask = StructureNotifyMask | ExposureMask; + xswa.background_pixel = 0; + xswa.border_pixel = 0; + + window = XCreateWindow(display, DefaultRootWindow(display), 0, 0, width, + height, 0, vinfo.depth, InputOutput, vinfo.visual, + CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, + &xswa); + + XStoreName(display, window, window_title); + XSetIconName(display, window, window_title); + + XSelectInput(display, window, StructureNotifyMask); + + XMapRaised(display, window); + + XEvent event; + do { + XNextEvent(display, &event); + } while (event.type != MapNotify || event.xmap.event != window); + + if (!XShmQueryExtension(display)) { + Destroy(); + return false; + } + + xv_complete = XShmGetEventBase(display) + ShmCompletion; + + XvAdaptorInfo* ai; + unsigned int p_num_adaptors; + + if (XvQueryAdaptors(display, DefaultRootWindow(display), &p_num_adaptors, + &ai) != + Success) { + Destroy(); + return false; + } + if (p_num_adaptors <= 0) { + XvFreeAdaptorInfo(ai); + Destroy(); + return false; + } + + xv_port = ai[p_num_adaptors - 1].base_id; + XvFreeAdaptorInfo(ai); + + if (xv_port == -1) { + Destroy(); + return false; + } + + gc = XCreateGC(display, window, 0, 0); + if (gc == NULL) { + Destroy(); + return false; + } + + Resize(width, height); + + return true; +} + +void XvRenderer::Destroy() { + if (image != NULL) { + XFree(image); + image = NULL; + } + + if (gc != NULL) { + XFreeGC(display, gc); + gc = NULL; + } + + if (display != NULL) { + XCloseDisplay(display); + display = NULL; + } +} + +XvRenderer* XvRenderer::Create(const char* window_title, size_t width, + size_t height) { + XvRenderer* xv_renderer = new XvRenderer(width, height); + if (!xv_renderer->Init(window_title)) { + // TODO(pbos): Add Xv-failed warning here? + delete xv_renderer; + return NULL; + } + return xv_renderer; +} + +XvRenderer::~XvRenderer() { Destroy(); } + +void XvRenderer::Resize(size_t width, size_t height) { + this->width = width; + this->height = height; + + if (image != NULL) { + XFree(image); + } + image = XvShmCreateImage(display, xv_port, GUID_I420_PLANAR, 0, width, height, + &shm_info); + assert(image != NULL); + + shm_info.shmid = shmget(IPC_PRIVATE, image->data_size, IPC_CREAT | 0777); + shm_info.shmaddr = image->data = + reinterpret_cast(shmat(shm_info.shmid, 0, 0)); + shm_info.readOnly = False; + + if (!XShmAttach(display, &shm_info)) { + abort(); + } + + XSizeHints* size_hints = XAllocSizeHints(); + if (size_hints == NULL) { + abort(); + } + size_hints->flags = PAspect; + size_hints->min_aspect.x = size_hints->max_aspect.x = width; + size_hints->min_aspect.y = size_hints->max_aspect.y = height; + XSetWMNormalHints(display, window, size_hints); + XFree(size_hints); + + XWindowChanges wc; + wc.width = width; + wc.height = height; + XConfigureWindow(display, window, CWWidth | CWHeight, &wc); +} + +void XvRenderer::RenderFrame(const webrtc::I420VideoFrame& frame, + int /*render_delay_ms*/) { + int size = webrtc::ExtractBuffer(frame, image->data_size, + reinterpret_cast(image->data)); + if (static_cast(frame.width()) != width || + static_cast(frame.height()) != height) { + Resize(static_cast(frame.width()), + static_cast(frame.height())); + } + assert(size > 0); + Window root; + int temp; + unsigned int window_width, window_height, u_temp; + + XGetGeometry(display, window, &root, &temp, &temp, &window_width, + &window_height, &u_temp, &u_temp); + + XvShmPutImage(display, xv_port, window, gc, image, 0, 0, image->width, + image->height, 0, 0, window_width, window_height, True); + + XFlush(display); + + XEvent event; + while (XPending(display)) { + XNextEvent(display, &event); + } +} +} // test +} // webrtc diff --git a/webrtc/video_engine/test/common/linux/xv_renderer.h b/webrtc/video_engine/test/common/linux/xv_renderer.h new file mode 100644 index 0000000000..df6b050261 --- /dev/null +++ b/webrtc/video_engine/test/common/linux/xv_renderer.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_XV_RENDERER_H_ +#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_XV_RENDERER_H_ + +#include +#include +#include + +#include "webrtc/video_engine/new_include/common.h" +#include "webrtc/video_engine/test/common/video_renderer.h" + +namespace webrtc { +namespace test { + +class XvRenderer : public VideoRenderer { + public: + ~XvRenderer(); + + virtual void RenderFrame(const webrtc::I420VideoFrame& frame, int delta) + OVERRIDE; + + static XvRenderer* Create(const char *window_title, size_t width, + size_t height); + + private: + XvRenderer(size_t width, size_t height); + + bool Init(const char *window_title); + void Resize(size_t width, size_t height); + void Destroy(); + + size_t width, height; + bool is_init; + + Display* display; + Window window; + GC gc; + XvImage* image; + XShmSegmentInfo shm_info; + int xv_port, xv_complete; +}; +} // test +} // webrtc + +#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_XV_RENDERER_H_ diff --git a/webrtc/video_engine/test/common/vcm_capturer.cc b/webrtc/video_engine/test/common/vcm_capturer.cc new file mode 100644 index 0000000000..62f0a854ba --- /dev/null +++ b/webrtc/video_engine/test/common/vcm_capturer.cc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/test/common/vcm_capturer.h" + +#include "webrtc/modules/video_capture/include/video_capture_factory.h" +#include "webrtc/video_engine/new_include/video_send_stream.h" + +namespace webrtc { +namespace test { + +VcmCapturer::VcmCapturer(webrtc::newapi::VideoSendStreamInput* input) + : VideoCapturer(input), started_(false), vcm(NULL), last_timestamp(0) {} + +bool VcmCapturer::Init(size_t width, size_t height, size_t target_fps) { + VideoCaptureModule::DeviceInfo* device_info = + VideoCaptureFactory::CreateDeviceInfo(42); // Any ID (42) will do. + + char device_name[256]; + char unique_name[256]; + if (device_info->GetDeviceName(0, device_name, sizeof(device_name), + unique_name, sizeof(unique_name)) != + 0) { + Destroy(); + return false; + } + + vcm = webrtc::VideoCaptureFactory::Create(0, unique_name); + vcm->RegisterCaptureDataCallback(*this); + + device_info->GetCapability(vcm->CurrentDeviceName(), 0, capability_); + delete device_info; + + capability_.width = static_cast(width); + capability_.height = static_cast(height); + capability_.maxFPS = static_cast(target_fps); + capability_.rawType = kVideoI420; + + if (vcm->StartCapture(capability_) != 0) { + Destroy(); + return false; + } + + assert(vcm->CaptureStarted()); + + return true; +} + +VcmCapturer* VcmCapturer::Create(newapi::VideoSendStreamInput* input, + size_t width, size_t height, + size_t target_fps) { + VcmCapturer* vcm_capturer = new VcmCapturer(input); + if (!vcm_capturer->Init(width, height, target_fps)) { + // TODO(pbos): Log a warning that this failed. + delete vcm_capturer; + return NULL; + } + return vcm_capturer; +} + + +void VcmCapturer::Start() { started_ = true; } + +void VcmCapturer::Stop() { started_ = false; } + +void VcmCapturer::Destroy() { + if (vcm == NULL) { + return; + } + + vcm->StopCapture(); + vcm->DeRegisterCaptureDataCallback(); + vcm->Release(); + + // TODO(pbos): How do I destroy the VideoCaptureModule? This still leaves + // non-freed memory. + vcm = NULL; +} + +VcmCapturer::~VcmCapturer() { Destroy(); } + +void VcmCapturer::OnIncomingCapturedFrame(const int32_t id, + I420VideoFrame& frame) { + if (last_timestamp == 0 || frame.timestamp() < last_timestamp) { + last_timestamp = frame.timestamp(); + } + + if (started_) { + input_->PutFrame(frame, frame.timestamp() - last_timestamp); + } + last_timestamp = frame.timestamp(); +} + +void VcmCapturer::OnIncomingCapturedEncodedFrame(const int32_t id, + VideoFrame& frame, + VideoCodecType codec_type) {} + +void VcmCapturer::OnCaptureDelayChanged(const int32_t id, const int32_t delay) { +} +} // test +} // webrtc diff --git a/webrtc/video_engine/test/common/vcm_capturer.h b/webrtc/video_engine/test/common/vcm_capturer.h new file mode 100644 index 0000000000..ac31825ef1 --- /dev/null +++ b/webrtc/video_engine/test/common/vcm_capturer.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_TEST_COMMON_VCM_CAPTURER_H_ +#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_VCM_CAPTURER_H_ + +#include "webrtc/common_types.h" +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/video_capture/include/video_capture.h" +#include "webrtc/video_engine/test/common/video_capturer.h" + +namespace webrtc { +namespace test { + +class VcmCapturer : public VideoCapturer, public VideoCaptureDataCallback { + public: + static VcmCapturer* Create(newapi::VideoSendStreamInput* input, size_t width, + size_t height, size_t target_fps); + virtual ~VcmCapturer(); + + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; + + virtual void OnIncomingCapturedFrame(const int32_t id, I420VideoFrame& frame) + OVERRIDE; + virtual void OnIncomingCapturedEncodedFrame(const int32_t id, + VideoFrame& frame, + VideoCodecType codec_type) + OVERRIDE; + virtual void OnCaptureDelayChanged(const int32_t id, const int32_t delay) + OVERRIDE; + + private: + explicit VcmCapturer(newapi::VideoSendStreamInput* input); + bool Init(size_t width, size_t height, size_t target_fps); + void Destroy(); + + bool started_; + VideoCaptureModule* vcm; + VideoCaptureCapability capability_; + + uint32_t last_timestamp; +}; +} // test +} // webrtc + +#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VCM_CAPTURER_H_ diff --git a/webrtc/video_engine/test/common/video_capturer.cc b/webrtc/video_engine/test/common/video_capturer.cc new file mode 100644 index 0000000000..c8e5477e44 --- /dev/null +++ b/webrtc/video_engine/test/common/video_capturer.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/test/common/video_capturer.h" +#include "webrtc/video_engine/test/common/vcm_capturer.h" + +namespace webrtc { +namespace test { + +class NullCapturer : public VideoCapturer { + public: + NullCapturer() : VideoCapturer(NULL) {} + virtual ~NullCapturer() {} + + virtual void Start() {} + virtual void Stop() {} +}; + +VideoCapturer::VideoCapturer(newapi::VideoSendStreamInput* input) + : input_(input) {} + +VideoCapturer* VideoCapturer::Create(newapi::VideoSendStreamInput* input) { + // TODO(pbos): These should be specified by command-line parameters. + size_t width = 640; + size_t height = 480; + size_t target_fps = 30; + + VcmCapturer* vcm_capturer = VcmCapturer::Create(input, width, height, + target_fps); + if (vcm_capturer != NULL) { + return vcm_capturer; + } + // TODO(pbos): Log a warning that this failed. + + // TODO(pbos): Add a pseudocapturer which generates frames. + + return new NullCapturer(); +} +} // test +} // webrtc diff --git a/webrtc/video_engine/test/common/video_capturer.h b/webrtc/video_engine/test/common/video_capturer.h new file mode 100644 index 0000000000..c142998a1d --- /dev/null +++ b/webrtc/video_engine/test/common/video_capturer.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_CAPTURER_H_ +#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_CAPTURER_H_ + +namespace webrtc { + +namespace newapi { +class VideoSendStreamInput; +} // newapi + +namespace test { + +class VideoCapturer { + public: + static VideoCapturer* Create(newapi::VideoSendStreamInput* input); + virtual ~VideoCapturer() {} + + virtual void Start() = 0; + virtual void Stop() = 0; + + protected: + explicit VideoCapturer(newapi::VideoSendStreamInput* input); + newapi::VideoSendStreamInput* input_; +}; +} // test +} // webrtc + +#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_CAPTURER_H diff --git a/webrtc/video_engine/test/common/video_renderer.cc b/webrtc/video_engine/test/common/video_renderer.cc new file mode 100644 index 0000000000..bc53fa7936 --- /dev/null +++ b/webrtc/video_engine/test/common/video_renderer.cc @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 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. + */ + +#include "webrtc/video_engine/test/common/video_renderer.h" + +#include "webrtc/modules/video_capture/include/video_capture_factory.h" +#include "webrtc/video_engine/new_include/video_send_stream.h" + +#ifdef WEBRTC_TEST_XV +#include "webrtc/video_engine/test/common/linux/xv_renderer.h" +#endif // WEBRTC_TEST_XV + +// Platform-specific renderers preferred over NullRenderer +#ifdef WEBRTC_TEST_GLX +#include "webrtc/video_engine/test/common/linux/glx_renderer.h" +#endif // WEBRTC_TEST_GLX + +// TODO(pbos): Mac renderer +// TODO(pbos): Windows renderer +// TODO(pbos): Android renderer + +namespace webrtc { +namespace test { + +class NullRenderer : public VideoRenderer { + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE {} +}; + +VideoRenderer* VideoRenderer::Create(const char* window_title) { + // TODO(pbos): Get these from command-line parameters. + int width = 640; + int height = 480; + +#ifdef WEBRTC_TEST_XV + XvRenderer* xv_renderer = XvRenderer::Create(window_title, width, height); + if (xv_renderer != NULL) { + return xv_renderer; + } +#endif // WEBRTC_TEST_XV +#ifdef WEBRTC_TEST_GLX + GlxRenderer* glx_renderer = GlxRenderer::Create(window_title, width, height); + if (glx_renderer != NULL) { + return glx_renderer; + } +#endif // WEBRTC_TEST_GLX + + // Avoid initialized-but-not-referenced errors when only building a + // NullRenderer + (void) width; + (void) height; + + return new NullRenderer(); +} +} // test +} // webrtc diff --git a/webrtc/video_engine/test/common/video_renderer.h b/webrtc/video_engine/test/common/video_renderer.h new file mode 100644 index 0000000000..60ca786a28 --- /dev/null +++ b/webrtc/video_engine/test/common/video_renderer.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 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 WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_RENDERER_H_ +#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_RENDERER_H_ + +#include "webrtc/video_engine/new_include/common.h" + +namespace webrtc { +namespace test { + +class VideoRenderer : public newapi::VideoRenderer { + public: + static VideoRenderer* Create(const char* window_title); + virtual ~VideoRenderer() {} +}; +} // test +} // webrtc + +#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_RENDERER_H_ diff --git a/webrtc/video_engine/test/loopback.cc b/webrtc/video_engine/test/loopback.cc new file mode 100644 index 0000000000..c8c3ae31e7 --- /dev/null +++ b/webrtc/video_engine/test/loopback.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2013 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. + */ + +#include + +#include +#include + +#include "webrtc/video_engine/new_include/video_engine.h" +#include "webrtc/video_engine/test/common/direct_transport.h" +#include "webrtc/video_engine/test/common/generate_ssrcs.h" +#include "webrtc/video_engine/test/common/video_capturer.h" +#include "webrtc/video_engine/test/common/video_renderer.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +class LoopbackTest : public ::testing::Test { + protected: + std::map reserved_ssrcs; +}; + +TEST_F(LoopbackTest, Test) { + test::VideoRenderer* local_preview = + test::VideoRenderer::Create("Local Preview"); + test::VideoRenderer* loopback_video = + test::VideoRenderer::Create("Loopback Video"); + + newapi::VideoEngine* video_engine = + newapi::VideoEngine::Create(webrtc::newapi::VideoEngineConfig()); + + test::DirectTransport transport(NULL); + newapi::VideoCall* call = video_engine->CreateCall(&transport); + + // Loopback, call sends to itself. + transport.SetReceiver(call->Receiver()); + + newapi::VideoSendStreamConfig send_config; + call->GetDefaultSendConfig(&send_config); + test::GenerateRandomSsrcs(&send_config, &reserved_ssrcs); + + send_config.local_renderer = local_preview; + + // TODO(pbos): Should be specified by command-line parameters. And not even + // visible in the test. Break it out to some get-test-defaults + // class + send_config.codec.width = 640; + send_config.codec.height = 480; + send_config.codec.minBitrate = 1000; + send_config.codec.startBitrate = 1500; + send_config.codec.maxBitrate = 2000; + + newapi::VideoSendStream* send_stream = call->CreateSendStream(send_config); + + test::VideoCapturer* camera = + test::VideoCapturer::Create(send_stream->Input()); + + newapi::VideoReceiveStreamConfig receive_config; + call->GetDefaultReceiveConfig(&receive_config); + receive_config.rtp.ssrc = send_config.rtp.ssrcs[0]; + receive_config.renderer = loopback_video; + + newapi::VideoReceiveStream* receive_stream = + call->CreateReceiveStream(receive_config); + + receive_stream->StartReceive(); + send_stream->StartSend(); + + camera->Start(); + + // TODO(pbos): Run this time limited (optionally), so it can run automated. + std::cout << ">> Press ENTER to continue..." << std::endl; + std::cin.ignore(std::numeric_limits::max(), '\n'); + + receive_stream->StopReceive(); + send_stream->StopSend(); + + // Stop sending + delete camera; + + call->DestroyReceiveStream(receive_stream); + call->DestroySendStream(send_stream); + + delete call; + delete video_engine; + + delete loopback_video; + delete local_preview; +} +} // webrtc + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/webrtc/video_engine/test/tests.gypi b/webrtc/video_engine/test/tests.gypi new file mode 100644 index 0000000000..d74881a44b --- /dev/null +++ b/webrtc/video_engine/test/tests.gypi @@ -0,0 +1,115 @@ +# Copyright (c) 2013 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. + +{ + 'variables': { + 'xv_renderer%': 0, + }, + 'conditions': [ + ['OS=="linux"', { + 'variables': { + 'glx_renderer%': 1, + }, + }, { + # OS != "linux" + 'variables': { + 'glx_renderer%': 0, + }, + }], + ], + 'targets': [ + { + 'target_name': 'video_tests_common', + 'type': 'static_library', + 'sources': [ + 'common/generate_ssrcs.h', + 'common/vcm_capturer.h', + 'common/vcm_capturer.cc', + 'common/video_capturer.cc', + 'common/video_capturer.h', + 'common/video_renderer.cc', + 'common/video_renderer.h', + ], + 'conditions': [ + ['glx_renderer==1', { + 'defines': [ + 'WEBRTC_TEST_GLX', + ], + 'sources' : [ + 'common/gl/gl_renderer.cc', + 'common/gl/gl_renderer.h', + 'common/linux/glx_renderer.cc', + 'common/linux/glx_renderer.h', + ], + }], + ['xv_renderer==1', { + 'defines': [ + 'WEBRTC_TEST_XV', + ], + 'sources': [ + 'common/linux/xv_renderer.cc', + 'common/linux/xv_renderer.h', + ], + }], + ], + 'direct_dependent_settings': { + 'conditions': [ + ['OS=="linux"', { + 'libraries': [ + '-lXext', + '-lX11', + '-lGL', + ], + }], + ['xv_renderer==1', { + 'libraries': [ + '-lXv', + ], + }], + #TODO(pbos) : These dependencies should not have to be here, they + # aren't used by test code directly, only by components + # used by the tests. + ['OS=="android"', { + 'libraries' : [ + '-lGLESv2', '-llog', + ], + }], + ['OS=="mac"', { + 'xcode_settings' : { + 'OTHER_LDFLAGS' : [ + '-framework Foundation', + '-framework AppKit', + '-framework Cocoa', + '-framework OpenGL', + '-framework CoreVideo', + '-framework CoreAudio', + '-framework AudioToolbox', + ], + }, + }], + ], + }, + 'dependencies': [ + '<(webrtc_root)/modules/modules.gyp:video_capture_module', + 'video_engine_core', + ], + }, + { + 'target_name': 'video_loopback', + 'type': 'executable', + 'sources': [ + 'loopback.cc', + ], + 'dependencies': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/modules/modules.gyp:video_capture_module', + 'video_tests_common', + ], + }, + ], +} diff --git a/webrtc/video_engine/video_engine.gyp b/webrtc/video_engine/video_engine.gyp index c0be24585f..cd0e9ad6e9 100644 --- a/webrtc/video_engine/video_engine.gyp +++ b/webrtc/video_engine/video_engine.gyp @@ -17,6 +17,7 @@ 'includes': [ 'test/libvietest/libvietest.gypi', 'test/auto_test/vie_auto_test.gypi', + 'test/tests.gypi', ], 'conditions': [ ['OS=="android"', { diff --git a/webrtc/video_engine/video_engine_core.gypi b/webrtc/video_engine/video_engine_core.gypi index 4296607e11..a8630eac51 100644 --- a/webrtc/video_engine/video_engine_core.gypi +++ b/webrtc/video_engine/video_engine_core.gypi @@ -128,6 +128,19 @@ 'vie_render_manager.cc', 'vie_sender.cc', 'vie_sync_module.cc', + + # New VideoEngine API + 'new_include/common.h', + 'new_include/video_engine.h', + 'new_include/video_receive_stream.h', + 'new_include/video_send_stream.h', + 'internal/video_engine.cc', + 'internal/video_call.cc', + 'internal/video_call.h', + 'internal/video_receive_stream.cc', + 'internal/video_receive_stream.h', + 'internal/video_send_stream.cc', + 'internal/video_send_stream.h', ], # source # TODO(jschuh): Bug 1348: fix size_t to int truncations. 'msvs_disabled_warnings': [ 4267, ],