Add SwapFrame() to VideoSendStreamInput.

Optionally prevents doing a frame copy when putting frames into a
VideoSendStream. PutFrame() is still there, which copies the frame.

Also removes time_since_capture_ms as a parameter, since
I420VideoFrame::render_time_ms() denotes when the frame was captured.

BUG=2657
R=mflodman@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/5119004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5265 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
pbos@webrtc.org 2013-12-11 16:26:16 +00:00
parent 4c3faa9d73
commit 724947b8ef
21 changed files with 199 additions and 164 deletions

View File

@ -49,84 +49,4 @@
],
},
], # targets
'conditions': [
['include_tests==1', {
'targets': [
{
'target_name': 'frame_generator',
'type': 'static_library',
'sources': [
'test/frame_generator.h',
'test/frame_generator.cc',
],
'dependencies': [
'common_video',
],
},
{
'target_name': 'common_video_unittests',
'type': '<(gtest_target_type)',
'dependencies': [
'common_video',
'<(DEPTH)/testing/gtest.gyp:gtest',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
'<(webrtc_root)/test/test.gyp:test_support_main',
],
'sources': [
'i420_video_frame_unittest.cc',
'libyuv/libyuv_unittest.cc',
'libyuv/scaler_unittest.cc',
'plane_unittest.cc',
'texture_video_frame_unittest.cc'
],
# Disable warnings to enable Win64 build, issue 1323.
'msvs_disabled_warnings': [
4267, # size_t to int truncation.
],
'conditions': [
# TODO(henrike): remove build_with_chromium==1 when the bots are
# using Chromium's buildbots.
['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', {
'dependencies': [
'<(DEPTH)/testing/android/native_test.gyp:native_test_native_code',
],
}],
],
},
], # targets
'conditions': [
# TODO(henrike): remove build_with_chromium==1 when the bots are using
# Chromium's buildbots.
['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', {
'targets': [
{
'target_name': 'common_video_unittests_apk_target',
'type': 'none',
'dependencies': [
'<(apk_tests_path):common_video_unittests_apk',
],
},
],
}],
['test_isolation_mode != "noop"', {
'targets': [
{
'target_name': 'common_video_unittests_run',
'type': 'none',
'dependencies': [
'common_video_unittests',
],
'includes': [
'../build/isolate.gypi',
'common_video_unittests.isolate',
],
'sources': [
'common_video_unittests.isolate',
],
},
],
}],
],
}], # include_tests
],
}

View File

@ -0,0 +1,76 @@
# 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.
{
'includes': ['../build/common.gypi'],
'targets': [
{
'target_name': 'common_video_unittests',
'type': '<(gtest_target_type)',
'dependencies': [
'<(webrtc_root)/common_video/common_video.gyp:common_video',
'<(DEPTH)/testing/gtest.gyp:gtest',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
'<(webrtc_root)/test/test.gyp:test_support_main',
],
'sources': [
'i420_video_frame_unittest.cc',
'libyuv/libyuv_unittest.cc',
'libyuv/scaler_unittest.cc',
'plane_unittest.cc',
'texture_video_frame_unittest.cc'
],
# Disable warnings to enable Win64 build, issue 1323.
'msvs_disabled_warnings': [
4267, # size_t to int truncation.
],
'conditions': [
# TODO(henrike): remove build_with_chromium==1 when the bots are
# using Chromium's buildbots.
['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', {
'dependencies': [
'<(DEPTH)/testing/android/native_test.gyp:native_test_native_code',
],
}],
],
},
], # targets
'conditions': [
# TODO(henrike): remove build_with_chromium==1 when the bots are using
# Chromium's buildbots.
['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', {
'targets': [
{
'target_name': 'common_video_unittests_apk_target',
'type': 'none',
'dependencies': [
'<(apk_tests_path):common_video_unittests_apk',
],
},
],
}],
['test_isolation_mode != "noop"', {
'targets': [
{
'target_name': 'common_video_unittests_run',
'type': 'none',
'dependencies': [
'common_video_unittests',
],
'includes': [
'../build/isolate.gypi',
'common_video_unittests.isolate',
],
'sources': [
'common_video_unittests.isolate',
],
},
],
}],
],
}

View File

@ -98,7 +98,7 @@
'<(webrtc_root)/modules/video_coding/codecs/vp8/vp8.gyp:webrtc_vp8',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
'<(webrtc_root)/test/test.gyp:test_support_main',
'<(webrtc_root)/common_video/common_video.gyp:frame_generator',
'<(webrtc_root)/test/test.gyp:frame_generator',
],
'sources': [
'audio_coding/main/acm2/acm_receiver_unittest.cc',

View File

@ -12,7 +12,6 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/common.h"
#include "webrtc/common_video/test/frame_generator.h"
#include "webrtc/modules/video_coding/codecs/interface/mock/mock_video_codec_interface.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
@ -22,6 +21,7 @@
#include "webrtc/modules/video_coding/main/test/test_util.h"
#include "webrtc/system_wrappers/interface/clock.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/test/frame_generator.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/gtest_disable.h"
@ -70,7 +70,7 @@ MATCHER_P(MatchesVp8StreamInfo, expected, "") {
class EmptyFrameGenerator : public FrameGenerator {
public:
virtual I420VideoFrame& NextFrame() OVERRIDE { return frame_; }
I420VideoFrame* NextFrame() OVERRIDE { frame_.ResetSize(); return &frame_; }
private:
I420VideoFrame frame_;
@ -180,7 +180,7 @@ class TestVideoSender : public ::testing::Test {
void AddFrame() {
assert(generator_.get());
sender_->AddVideoFrame(generator_->NextFrame(), NULL, NULL);
sender_->AddVideoFrame(*generator_->NextFrame(), NULL, NULL);
}
SimulatedClock clock_;

View File

@ -7,7 +7,7 @@
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/common_video/test/frame_generator.h"
#include "webrtc/test/frame_generator.h"
#include <math.h>
#include <stdio.h>
@ -21,29 +21,32 @@ namespace {
class ChromaGenerator : public FrameGenerator {
public:
ChromaGenerator(size_t width, size_t height) : angle_(0.0) {
ChromaGenerator(size_t width, size_t height)
: angle_(0.0), width_(width), height_(height) {
assert(width > 0);
assert(height > 0);
frame_.CreateEmptyFrame(static_cast<int>(width),
static_cast<int>(height),
static_cast<int>(width),
static_cast<int>((width + 1) / 2),
static_cast<int>((width + 1) / 2));
memset(frame_.buffer(kYPlane), 0x80, frame_.allocated_size(kYPlane));
}
virtual I420VideoFrame& NextFrame() OVERRIDE {
virtual I420VideoFrame* NextFrame() OVERRIDE {
frame_.CreateEmptyFrame(static_cast<int>(width_),
static_cast<int>(height_),
static_cast<int>(width_),
static_cast<int>((width_ + 1) / 2),
static_cast<int>((width_ + 1) / 2));
angle_ += 30.0;
uint8_t u = fabs(sin(angle_)) * 0xFF;
uint8_t v = fabs(cos(angle_)) * 0xFF;
memset(frame_.buffer(kYPlane), 0x80, frame_.allocated_size(kYPlane));
memset(frame_.buffer(kUPlane), u, frame_.allocated_size(kUPlane));
memset(frame_.buffer(kVPlane), v, frame_.allocated_size(kVPlane));
return frame_;
return &frame_;
}
private:
double angle_;
size_t width_;
size_t height_;
I420VideoFrame frame_;
};
@ -57,11 +60,6 @@ class YuvFileGenerator : public FrameGenerator {
frame_size_ = CalcBufferSize(
kI420, static_cast<int>(width_), static_cast<int>(height_));
frame_buffer_ = new uint8_t[frame_size_];
frame_.CreateEmptyFrame(static_cast<int>(width),
static_cast<int>(height),
static_cast<int>(width),
static_cast<int>((width + 1) / 2),
static_cast<int>((width + 1) / 2));
}
virtual ~YuvFileGenerator() {
@ -69,13 +67,19 @@ class YuvFileGenerator : public FrameGenerator {
delete[] frame_buffer_;
}
virtual I420VideoFrame& NextFrame() OVERRIDE {
virtual I420VideoFrame* NextFrame() OVERRIDE {
size_t count = fread(frame_buffer_, 1, frame_size_, file_);
if (count < frame_size_) {
rewind(file_);
return NextFrame();
}
frame_.CreateEmptyFrame(static_cast<int>(width_),
static_cast<int>(height_),
static_cast<int>(width_),
static_cast<int>((width_ + 1) / 2),
static_cast<int>((width_ + 1) / 2));
ConvertToI420(kI420,
frame_buffer_,
0,
@ -85,7 +89,7 @@ class YuvFileGenerator : public FrameGenerator {
0,
kRotateNone,
&frame_);
return frame_;
return &frame_;
}
private:

View File

@ -22,7 +22,7 @@ class FrameGenerator {
virtual ~FrameGenerator() {}
// Returns video frame that remains valid until next call.
virtual I420VideoFrame& NextFrame() = 0;
virtual I420VideoFrame* NextFrame() = 0;
static FrameGenerator* Create(size_t width, size_t height);
static FrameGenerator* CreateFromYuvFile(const char* file,

View File

@ -10,7 +10,7 @@
#include "webrtc/test/frame_generator_capturer.h"
#include "webrtc/common_video/test/frame_generator.h"
#include "webrtc/test/frame_generator.h"
#include "webrtc/system_wrappers/interface/clock.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
@ -111,11 +111,9 @@ void FrameGeneratorCapturer::InsertFrame() {
{
CriticalSectionScoped cs(lock_.get());
if (sending_) {
int64_t time_before = clock_->CurrentNtpInMilliseconds();
I420VideoFrame& frame = frame_generator_->NextFrame();
frame.set_render_time_ms(time_before);
int64_t time_after = clock_->CurrentNtpInMilliseconds();
input_->PutFrame(frame, static_cast<uint32_t>(time_after - time_before));
I420VideoFrame* frame = frame_generator_->NextFrame();
frame->set_render_time_ms(clock_->CurrentNtpInMilliseconds());
input_->SwapFrame(frame);
}
}
tick_->Wait(WEBRTC_EVENT_INFINITE);

View File

@ -41,6 +41,17 @@
'channel_transport/udp_transport_impl.h',
],
},
{
'target_name': 'frame_generator',
'type': 'static_library',
'sources': [
'frame_generator.cc',
'frame_generator.h',
],
'dependencies': [
'<(webrtc_root)/common_video/common_video.gyp:common_video',
],
},
{
'target_name': 'test_support',
'type': 'static_library',

View File

@ -17,7 +17,7 @@ namespace webrtc {
namespace test {
VcmCapturer::VcmCapturer(webrtc::VideoSendStreamInput* input)
: VideoCapturer(input), started_(false), vcm_(NULL), last_timestamp_(0) {}
: VideoCapturer(input), started_(false), vcm_(NULL) {}
bool VcmCapturer::Init(size_t width, size_t height, size_t target_fps) {
VideoCaptureModule::DeviceInfo* device_info =
@ -88,14 +88,8 @@ 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();
if (started_)
input_->SwapFrame(&frame);
}
void VcmCapturer::OnCaptureDelayChanged(const int32_t id, const int32_t delay) {

View File

@ -40,8 +40,6 @@ class VcmCapturer : public VideoCapturer, public VideoCaptureDataCallback {
bool started_;
VideoCaptureModule* vcm_;
VideoCaptureCapability capability_;
uint32_t last_timestamp_;
};
} // test
} // webrtc

View File

@ -119,8 +119,8 @@
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
'<(webrtc_root)/modules/modules.gyp:video_capture_module',
'<(webrtc_root)/modules/modules.gyp:media_file',
'<(webrtc_root)/test/test.gyp:frame_generator',
'<(webrtc_root)/test/test.gyp:test_support',
'<(webrtc_root)/common_video/common_video.gyp:frame_generator',
],
},
],

View File

@ -17,7 +17,6 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/call.h"
#include "webrtc/common_video/test/frame_generator.h"
#include "webrtc/frame_callback.h"
#include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
@ -25,20 +24,21 @@
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/test/direct_transport.h"
#include "webrtc/test/fake_audio_device.h"
#include "webrtc/test/fake_decoder.h"
#include "webrtc/test/fake_encoder.h"
#include "webrtc/test/frame_generator.h"
#include "webrtc/test/frame_generator_capturer.h"
#include "webrtc/test/rtp_rtcp_observer.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/perf_test.h"
#include "webrtc/video/transport_adapter.h"
#include "webrtc/voice_engine/include/voe_base.h"
#include "webrtc/voice_engine/include/voe_codec.h"
#include "webrtc/voice_engine/include/voe_network.h"
#include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
#include "webrtc/voice_engine/include/voe_video_sync.h"
#include "webrtc/test/direct_transport.h"
#include "webrtc/test/fake_audio_device.h"
#include "webrtc/test/fake_decoder.h"
#include "webrtc/test/fake_encoder.h"
#include "webrtc/test/frame_generator_capturer.h"
#include "webrtc/test/rtp_rtcp_observer.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/perf_test.h"
namespace webrtc {
@ -336,7 +336,7 @@ TEST_F(CallTest, TransmitsFirstFrame) {
scoped_ptr<test::FrameGenerator> frame_generator(test::FrameGenerator::Create(
send_config_.codec.width, send_config_.codec.height));
send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
send_stream_->Input()->SwapFrame(frame_generator->NextFrame());
EXPECT_EQ(kEventSignaled, renderer.Wait())
<< "Timed out while waiting for the frame to render.";
@ -499,7 +499,7 @@ TEST_F(CallTest, UsesFrameCallbacks) {
// check that the callbacks are done after processing video.
scoped_ptr<test::FrameGenerator> frame_generator(
test::FrameGenerator::Create(kWidth / 2, kHeight / 2));
send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
send_stream_->Input()->SwapFrame(frame_generator->NextFrame());
EXPECT_EQ(kEventSignaled, pre_encode_callback.Wait())
<< "Timed out while waiting for pre-encode callback.";
@ -1147,7 +1147,7 @@ TEST_F(CallTest, ObserversEncodedFrames) {
scoped_ptr<test::FrameGenerator> frame_generator(test::FrameGenerator::Create(
send_config_.codec.width, send_config_.codec.height));
send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
send_stream_->Input()->SwapFrame(frame_generator->NextFrame());
EXPECT_EQ(kEventSignaled, post_encode_observer.Wait())
<< "Timed out while waiting for send-side encoded-frame callback.";

View File

@ -123,8 +123,11 @@ class VideoAnalyzer : public PacketReceiver,
return receiver_->DeliverPacket(packet, length);
}
virtual void PutFrame(const I420VideoFrame& video_frame,
uint32_t delta_capture_ms) OVERRIDE {
virtual void PutFrame(const I420VideoFrame& video_frame) OVERRIDE {
ADD_FAILURE() << "PutFrame() should not have been called in this test.";
}
virtual void SwapFrame(I420VideoFrame* video_frame) OVERRIDE {
I420VideoFrame* copy = NULL;
{
CriticalSectionScoped cs(crit_.get());
@ -136,7 +139,7 @@ class VideoAnalyzer : public PacketReceiver,
if (copy == NULL)
copy = new I420VideoFrame();
copy->CopyFrame(video_frame);
copy->CopyFrame(*video_frame);
copy->set_timestamp(copy->render_time_ms() * 90);
{
@ -147,7 +150,7 @@ class VideoAnalyzer : public PacketReceiver,
frames_.push_back(copy);
}
input_->PutFrame(video_frame, delta_capture_ms);
input_->SwapFrame(video_frame);
}
virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {

View File

@ -211,31 +211,24 @@ VideoSendStream::~VideoSendStream() {
rtp_rtcp_->Release();
}
void VideoSendStream::PutFrame(const I420VideoFrame& frame,
uint32_t time_since_capture_ms) {
// TODO(pbos): frame_copy should happen after the VideoProcessingModule has
// resized the frame.
I420VideoFrame frame_copy;
frame_copy.CopyFrame(frame);
void VideoSendStream::PutFrame(const I420VideoFrame& frame) {
input_frame_.CopyFrame(frame);
SwapFrame(&input_frame_);
}
ViEVideoFrameI420 vf;
void VideoSendStream::SwapFrame(I420VideoFrame* frame) {
// TODO(pbos): Warn if frame is "too far" into the future, or too old. This
// would help detect if frame's being used without NTP.
// TO REVIEWER: Is there any good check for this? Should it be
// skipped?
if (frame != &input_frame_)
input_frame_.SwapFrame(frame);
// 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();
// TODO(pbos): Local rendering should not be done on the capture thread.
if (config_.local_renderer != NULL)
config_.local_renderer->RenderFrame(input_frame_, 0);
external_capture_->IncomingFrameI420(vf, frame.render_time_ms());
if (config_.local_renderer != NULL) {
config_.local_renderer->RenderFrame(frame, 0);
}
external_capture_->SwapFrame(&input_frame_);
}
VideoSendStreamInput* VideoSendStream::Input() { return this; }

View File

@ -46,8 +46,9 @@ class VideoSendStream : public webrtc::VideoSendStream,
virtual ~VideoSendStream();
virtual void PutFrame(const I420VideoFrame& frame,
uint32_t time_since_capture_ms) OVERRIDE;
virtual void PutFrame(const I420VideoFrame& frame) OVERRIDE;
virtual void SwapFrame(I420VideoFrame* frame) OVERRIDE;
virtual VideoSendStreamInput* Input() OVERRIDE;
@ -62,6 +63,7 @@ class VideoSendStream : public webrtc::VideoSendStream,
bool DeliverRtcp(const uint8_t* packet, size_t length);
private:
I420VideoFrame input_frame_;
TransportAdapter transport_adapter_;
EncodedFrameCallbackAdapter encoded_frame_proxy_;
scoped_ptr<CriticalSectionWrapper> codec_lock_;

View File

@ -368,6 +368,30 @@ class FakeReceiveStatistics : public NullReceiveStatistics {
StatisticianMap stats_map_;
};
TEST_F(VideoSendStreamTest, SwapsI420VideoFrames) {
static const size_t kWidth = 320;
static const size_t kHeight = 240;
test::NullTransport transport;
Call::Config call_config(&transport);
scoped_ptr<Call> call(Call::Create(call_config));
VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
VideoSendStream* video_send_stream = call->CreateVideoSendStream(send_config);
video_send_stream->StartSending();
I420VideoFrame frame;
frame.CreateEmptyFrame(
kWidth, kHeight, kWidth, (kWidth + 1) / 2, (kWidth + 1) / 2);
uint8_t* old_y_buffer = frame.buffer(kYPlane);
video_send_stream->Input()->SwapFrame(&frame);
EXPECT_NE(frame.buffer(kYPlane), old_y_buffer);
call->DestroyVideoSendStream(video_send_stream);
}
TEST_F(VideoSendStreamTest, SupportsFec) {
static const int kRedPayloadType = 118;
static const int kUlpfecPayloadType = 119;

View File

@ -19,6 +19,7 @@
#define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_CAPTURE_H_
#include "webrtc/common_types.h"
#include "webrtc/common_video/interface/i420_video_frame.h"
namespace webrtc {
@ -117,6 +118,8 @@ class WEBRTC_DLLEXPORT ViEExternalCapture {
virtual int IncomingFrameI420(
const ViEVideoFrameI420& video_frame,
unsigned long long capture_time = 0) = 0;
virtual void SwapFrame(I420VideoFrame* frame) {}
};
// This class declares an abstract interface for a user defined observer. It is

View File

@ -363,6 +363,11 @@ int ViECapturer::IncomingFrameI420(const ViEVideoFrameI420& video_frame,
capture_time);
}
void ViECapturer::SwapFrame(I420VideoFrame* frame) {
external_capture_module_->IncomingI420VideoFrame(frame,
frame->render_time_ms());
}
void ViECapturer::OnIncomingCapturedFrame(const int32_t capture_id,
I420VideoFrame& video_frame) {
WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_),

View File

@ -76,6 +76,8 @@ class ViECapturer
virtual int IncomingFrameI420(const ViEVideoFrameI420& video_frame,
unsigned long long capture_time = 0); // NOLINT
virtual void SwapFrame(I420VideoFrame* frame) OVERRIDE;
// Start/Stop.
int32_t Start(
const CaptureCapability& capture_capability = CaptureCapability());

View File

@ -26,10 +26,11 @@ class VideoEncoder;
// Class to deliver captured frame to the video send stream.
class VideoSendStreamInput {
public:
// TODO(mflodman) Replace time_since_capture_ms when I420VideoFrame uses NTP
// time.
virtual void PutFrame(const I420VideoFrame& video_frame,
uint32_t time_since_capture_ms) = 0;
// These methods do not lock internally and must be called sequentially.
// If your application switches input sources synchronization must be done
// externally to make sure that any old frames are not delivered concurrently.
virtual void PutFrame(const I420VideoFrame& video_frame) = 0;
virtual void SwapFrame(I420VideoFrame* video_frame) = 0;
protected:
virtual ~VideoSendStreamInput() {}

View File

@ -39,6 +39,7 @@
'conditions': [
['include_tests==1', {
'dependencies': [
'common_video/common_video_unittests.gyp:*',
'system_wrappers/source/system_wrappers_tests.gyp:*',
'test/metrics.gyp:*',
'test/test.gyp:*',