From 724947b8efa44d15d699b471020005450590f5b6 Mon Sep 17 00:00:00 2001 From: "pbos@webrtc.org" Date: Wed, 11 Dec 2013 16:26:16 +0000 Subject: [PATCH] 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 --- webrtc/common_video/common_video.gyp | 80 ------------------- .../common_video/common_video_unittests.gyp | 76 ++++++++++++++++++ webrtc/modules/modules.gyp | 2 +- .../main/source/video_sender_unittest.cc | 6 +- .../test/frame_generator.cc | 38 +++++---- .../{common_video => }/test/frame_generator.h | 2 +- webrtc/test/frame_generator_capturer.cc | 10 +-- webrtc/test/test.gyp | 11 +++ webrtc/test/vcm_capturer.cc | 12 +-- webrtc/test/vcm_capturer.h | 2 - webrtc/test/webrtc_test_common.gyp | 2 +- webrtc/video/call_tests.cc | 24 +++--- webrtc/video/full_stack.cc | 11 ++- webrtc/video/video_send_stream.cc | 37 ++++----- webrtc/video/video_send_stream.h | 6 +- webrtc/video/video_send_stream_tests.cc | 24 ++++++ webrtc/video_engine/include/vie_capture.h | 3 + webrtc/video_engine/vie_capturer.cc | 5 ++ webrtc/video_engine/vie_capturer.h | 2 + webrtc/video_send_stream.h | 9 ++- webrtc/webrtc.gyp | 1 + 21 files changed, 199 insertions(+), 164 deletions(-) create mode 100644 webrtc/common_video/common_video_unittests.gyp rename webrtc/{common_video => }/test/frame_generator.cc (74%) rename webrtc/{common_video => }/test/frame_generator.h (96%) diff --git a/webrtc/common_video/common_video.gyp b/webrtc/common_video/common_video.gyp index f4d9ceb17c..97cb94197e 100644 --- a/webrtc/common_video/common_video.gyp +++ b/webrtc/common_video/common_video.gyp @@ -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 - ], } diff --git a/webrtc/common_video/common_video_unittests.gyp b/webrtc/common_video/common_video_unittests.gyp new file mode 100644 index 0000000000..9523361cda --- /dev/null +++ b/webrtc/common_video/common_video_unittests.gyp @@ -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', + ], + }, + ], + }], + ], +} diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 53e7b316fe..e33816c6e9 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -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', diff --git a/webrtc/modules/video_coding/main/source/video_sender_unittest.cc b/webrtc/modules/video_coding/main/source/video_sender_unittest.cc index bdf294725a..513a99ee74 100644 --- a/webrtc/modules/video_coding/main/source/video_sender_unittest.cc +++ b/webrtc/modules/video_coding/main/source/video_sender_unittest.cc @@ -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_; diff --git a/webrtc/common_video/test/frame_generator.cc b/webrtc/test/frame_generator.cc similarity index 74% rename from webrtc/common_video/test/frame_generator.cc rename to webrtc/test/frame_generator.cc index 062bd17d07..4dd76aeea6 100644 --- a/webrtc/common_video/test/frame_generator.cc +++ b/webrtc/test/frame_generator.cc @@ -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 #include @@ -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(width), - static_cast(height), - static_cast(width), - static_cast((width + 1) / 2), - static_cast((width + 1) / 2)); - memset(frame_.buffer(kYPlane), 0x80, frame_.allocated_size(kYPlane)); } - virtual I420VideoFrame& NextFrame() OVERRIDE { + virtual I420VideoFrame* NextFrame() OVERRIDE { + frame_.CreateEmptyFrame(static_cast(width_), + static_cast(height_), + static_cast(width_), + static_cast((width_ + 1) / 2), + static_cast((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(width_), static_cast(height_)); frame_buffer_ = new uint8_t[frame_size_]; - frame_.CreateEmptyFrame(static_cast(width), - static_cast(height), - static_cast(width), - static_cast((width + 1) / 2), - static_cast((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(width_), + static_cast(height_), + static_cast(width_), + static_cast((width_ + 1) / 2), + static_cast((width_ + 1) / 2)); + ConvertToI420(kI420, frame_buffer_, 0, @@ -85,7 +89,7 @@ class YuvFileGenerator : public FrameGenerator { 0, kRotateNone, &frame_); - return frame_; + return &frame_; } private: diff --git a/webrtc/common_video/test/frame_generator.h b/webrtc/test/frame_generator.h similarity index 96% rename from webrtc/common_video/test/frame_generator.h rename to webrtc/test/frame_generator.h index 823370091d..fe10612fb5 100644 --- a/webrtc/common_video/test/frame_generator.h +++ b/webrtc/test/frame_generator.h @@ -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, diff --git a/webrtc/test/frame_generator_capturer.cc b/webrtc/test/frame_generator_capturer.cc index e04961dd21..6570c6f154 100644 --- a/webrtc/test/frame_generator_capturer.cc +++ b/webrtc/test/frame_generator_capturer.cc @@ -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(time_after - time_before)); + I420VideoFrame* frame = frame_generator_->NextFrame(); + frame->set_render_time_ms(clock_->CurrentNtpInMilliseconds()); + input_->SwapFrame(frame); } } tick_->Wait(WEBRTC_EVENT_INFINITE); diff --git a/webrtc/test/test.gyp b/webrtc/test/test.gyp index 09851f5a0e..0051bcee95 100644 --- a/webrtc/test/test.gyp +++ b/webrtc/test/test.gyp @@ -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', diff --git a/webrtc/test/vcm_capturer.cc b/webrtc/test/vcm_capturer.cc index cbf7b1ffea..a5820bfe11 100644 --- a/webrtc/test/vcm_capturer.cc +++ b/webrtc/test/vcm_capturer.cc @@ -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) { diff --git a/webrtc/test/vcm_capturer.h b/webrtc/test/vcm_capturer.h index 40befe8d1b..dde3edc2f7 100644 --- a/webrtc/test/vcm_capturer.h +++ b/webrtc/test/vcm_capturer.h @@ -40,8 +40,6 @@ class VcmCapturer : public VideoCapturer, public VideoCaptureDataCallback { bool started_; VideoCaptureModule* vcm_; VideoCaptureCapability capability_; - - uint32_t last_timestamp_; }; } // test } // webrtc diff --git a/webrtc/test/webrtc_test_common.gyp b/webrtc/test/webrtc_test_common.gyp index c7f6df5f56..eae66a0440 100644 --- a/webrtc/test/webrtc_test_common.gyp +++ b/webrtc/test/webrtc_test_common.gyp @@ -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', ], }, ], diff --git a/webrtc/video/call_tests.cc b/webrtc/video/call_tests.cc index 6c247e6755..5e4d6820d8 100644 --- a/webrtc/video/call_tests.cc +++ b/webrtc/video/call_tests.cc @@ -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 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 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 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."; diff --git a/webrtc/video/full_stack.cc b/webrtc/video/full_stack.cc index f4e07e0495..1181bfe488 100644 --- a/webrtc/video/full_stack.cc +++ b/webrtc/video/full_stack.cc @@ -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 { diff --git a/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc index d90c733c14..2b83a48c30 100644 --- a/webrtc/video/video_send_stream.cc +++ b/webrtc/video/video_send_stream.cc @@ -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; } diff --git a/webrtc/video/video_send_stream.h b/webrtc/video/video_send_stream.h index 0881d91817..addb045def 100644 --- a/webrtc/video/video_send_stream.h +++ b/webrtc/video/video_send_stream.h @@ -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 codec_lock_; diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc index d45a9c3efa..ae33e81ea7 100644 --- a/webrtc/video/video_send_stream_tests.cc +++ b/webrtc/video/video_send_stream_tests.cc @@ -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::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; diff --git a/webrtc/video_engine/include/vie_capture.h b/webrtc/video_engine/include/vie_capture.h index 2174d5dc6f..cee3626510 100644 --- a/webrtc/video_engine/include/vie_capture.h +++ b/webrtc/video_engine/include/vie_capture.h @@ -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 diff --git a/webrtc/video_engine/vie_capturer.cc b/webrtc/video_engine/vie_capturer.cc index 9a2e3fcfb9..aec5b39a1d 100644 --- a/webrtc/video_engine/vie_capturer.cc +++ b/webrtc/video_engine/vie_capturer.cc @@ -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_), diff --git a/webrtc/video_engine/vie_capturer.h b/webrtc/video_engine/vie_capturer.h index 9da187ab33..1fa2b53df1 100644 --- a/webrtc/video_engine/vie_capturer.h +++ b/webrtc/video_engine/vie_capturer.h @@ -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()); diff --git a/webrtc/video_send_stream.h b/webrtc/video_send_stream.h index 2358042e5a..9d4695dfe7 100644 --- a/webrtc/video_send_stream.h +++ b/webrtc/video_send_stream.h @@ -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() {} diff --git a/webrtc/webrtc.gyp b/webrtc/webrtc.gyp index 7da4433c4c..ed101b6624 100644 --- a/webrtc/webrtc.gyp +++ b/webrtc/webrtc.gyp @@ -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:*',