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:
parent
4c3faa9d73
commit
724947b8ef
@ -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
|
||||
],
|
||||
}
|
||||
|
76
webrtc/common_video/common_video_unittests.gyp
Normal file
76
webrtc/common_video/common_video_unittests.gyp
Normal 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',
|
||||
],
|
||||
},
|
||||
],
|
||||
}],
|
||||
],
|
||||
}
|
@ -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',
|
||||
|
@ -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_;
|
||||
|
@ -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:
|
@ -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,
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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) {
|
||||
|
@ -40,8 +40,6 @@ class VcmCapturer : public VideoCapturer, public VideoCaptureDataCallback {
|
||||
bool started_;
|
||||
VideoCaptureModule* vcm_;
|
||||
VideoCaptureCapability capability_;
|
||||
|
||||
uint32_t last_timestamp_;
|
||||
};
|
||||
} // test
|
||||
} // webrtc
|
||||
|
@ -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',
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -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.";
|
||||
|
@ -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 {
|
||||
|
@ -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; }
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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_),
|
||||
|
@ -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());
|
||||
|
@ -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() {}
|
||||
|
@ -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:*',
|
||||
|
Loading…
x
Reference in New Issue
Block a user