Add tests of texture frames in video_send_stream_test.

Also fix a bug in ViEFrameProviderBase::DeliverFrame that
a texture frame was only delivered to the first callback.

BUG=chromium:362437
TEST=Run video engine test and webrtc call on CrOS.
R=kjellander@webrtc.org, pbos@webrtc.org, stefan@webrtc.org, wuchengli@google.com

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6506 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
wuchengli@chromium.org 2014-06-20 12:04:05 +00:00
parent 83785d37d1
commit f425b55eeb
3 changed files with 212 additions and 35 deletions

View File

@ -13,6 +13,8 @@
#include "webrtc/call.h"
#include "webrtc/common_video/interface/i420_video_frame.h"
#include "webrtc/common_video/interface/native_handle.h"
#include "webrtc/common_video/interface/texture_video_frame.h"
#include "webrtc/frame_callback.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
@ -20,7 +22,9 @@
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/system_wrappers/interface/ref_count.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/system_wrappers/interface/scoped_vector.h"
#include "webrtc/system_wrappers/interface/sleep.h"
#include "webrtc/system_wrappers/interface/thread_wrapper.h"
#include "webrtc/test/direct_transport.h"
@ -38,6 +42,23 @@ namespace webrtc {
enum VideoFormat { kGeneric, kVP8, };
void ExpectEqualFrames(const I420VideoFrame& frame1,
const I420VideoFrame& frame2);
void ExpectEqualTextureFrames(const I420VideoFrame& frame1,
const I420VideoFrame& frame2);
void ExpectEqualBufferFrames(const I420VideoFrame& frame1,
const I420VideoFrame& frame2);
void ExpectEqualFramesVector(const std::vector<I420VideoFrame*>& frames1,
const std::vector<I420VideoFrame*>& frames2);
I420VideoFrame* CreateI420VideoFrame(int width, int height, uint8_t data);
class FakeNativeHandle : public NativeHandle {
public:
FakeNativeHandle() {}
virtual ~FakeNativeHandle() {}
virtual void* GetHandle() { return NULL; }
};
class VideoSendStreamTest : public ::testing::Test {
public:
VideoSendStreamTest()
@ -1219,4 +1240,151 @@ TEST_F(VideoSendStreamTest, MinTransmitBitrateRespectsRemb) {
call->DestroyVideoSendStream(send_stream_);
}
TEST_F(VideoSendStreamTest, CapturesTextureAndI420VideoFrames) {
class FrameObserver : public I420FrameCallback {
public:
FrameObserver() : output_frame_event_(EventWrapper::Create()) {}
void FrameCallback(I420VideoFrame* video_frame) OVERRIDE {
// Clone the frame because the caller owns it.
output_frames_.push_back(video_frame->CloneFrame());
output_frame_event_->Set();
}
void WaitOutputFrame() {
const unsigned long kWaitFrameTimeoutMs = 3000;
EXPECT_EQ(kEventSignaled, output_frame_event_->Wait(kWaitFrameTimeoutMs))
<< "Timeout while waiting for output frames.";
}
const std::vector<I420VideoFrame*>& output_frames() const {
return output_frames_.get();
}
private:
// Delivered output frames.
ScopedVector<I420VideoFrame> output_frames_;
// Indicate an output frame has arrived.
scoped_ptr<EventWrapper> output_frame_event_;
};
// Initialize send stream.
test::NullTransport transport;
Call::Config call_config(&transport);
scoped_ptr<Call> call(Call::Create(call_config));
CreateTestConfig(call.get(), 1);
FrameObserver observer;
send_config_.pre_encode_callback = &observer;
send_stream_ =
call->CreateVideoSendStream(send_config_, video_streams_, NULL);
// Prepare five input frames. Send I420VideoFrame and TextureVideoFrame
// alternatively.
ScopedVector<I420VideoFrame> input_frames;
int width = static_cast<int>(video_streams_[0].width);
int height = static_cast<int>(video_streams_[0].height);
webrtc::RefCountImpl<FakeNativeHandle>* handle1 =
new webrtc::RefCountImpl<FakeNativeHandle>();
webrtc::RefCountImpl<FakeNativeHandle>* handle2 =
new webrtc::RefCountImpl<FakeNativeHandle>();
webrtc::RefCountImpl<FakeNativeHandle>* handle3 =
new webrtc::RefCountImpl<FakeNativeHandle>();
input_frames.push_back(new TextureVideoFrame(handle1, width, height, 1, 1));
input_frames.push_back(new TextureVideoFrame(handle2, width, height, 2, 2));
input_frames.push_back(CreateI420VideoFrame(width, height, 1));
input_frames.push_back(CreateI420VideoFrame(width, height, 2));
input_frames.push_back(new TextureVideoFrame(handle3, width, height, 3, 3));
send_stream_->Start();
for (size_t i = 0; i < input_frames.size(); i++) {
// Make a copy of the input frame because the buffer will be swapped.
scoped_ptr<I420VideoFrame> frame(input_frames[i]->CloneFrame());
send_stream_->Input()->SwapFrame(frame.get());
// Do not send the next frame too fast, so the frame dropper won't drop it.
if (i < input_frames.size() - 1)
SleepMs(1000 / video_streams_[0].max_framerate);
// Wait until the output frame is received before sending the next input
// frame. Or the previous input frame may be replaced without delivering.
observer.WaitOutputFrame();
}
send_stream_->Stop();
// Test if the input and output frames are the same. render_time_ms and
// timestamp are not compared because capturer sets those values.
ExpectEqualFramesVector(input_frames.get(), observer.output_frames());
call->DestroyVideoSendStream(send_stream_);
}
void ExpectEqualFrames(const I420VideoFrame& frame1,
const I420VideoFrame& frame2) {
if (frame1.native_handle() != NULL || frame2.native_handle() != NULL)
ExpectEqualTextureFrames(frame1, frame2);
else
ExpectEqualBufferFrames(frame1, frame2);
}
void ExpectEqualTextureFrames(const I420VideoFrame& frame1,
const I420VideoFrame& frame2) {
EXPECT_EQ(frame1.native_handle(), frame2.native_handle());
EXPECT_EQ(frame1.width(), frame2.width());
EXPECT_EQ(frame1.height(), frame2.height());
}
void ExpectEqualBufferFrames(const I420VideoFrame& frame1,
const I420VideoFrame& frame2) {
EXPECT_EQ(frame1.width(), frame2.width());
EXPECT_EQ(frame1.height(), frame2.height());
EXPECT_EQ(frame1.stride(kYPlane), frame2.stride(kYPlane));
EXPECT_EQ(frame1.stride(kUPlane), frame2.stride(kUPlane));
EXPECT_EQ(frame1.stride(kVPlane), frame2.stride(kVPlane));
EXPECT_EQ(frame1.ntp_time_ms(), frame2.ntp_time_ms());
ASSERT_EQ(frame1.allocated_size(kYPlane), frame2.allocated_size(kYPlane));
EXPECT_EQ(0,
memcmp(frame1.buffer(kYPlane),
frame2.buffer(kYPlane),
frame1.allocated_size(kYPlane)));
ASSERT_EQ(frame1.allocated_size(kUPlane), frame2.allocated_size(kUPlane));
EXPECT_EQ(0,
memcmp(frame1.buffer(kUPlane),
frame2.buffer(kUPlane),
frame1.allocated_size(kUPlane)));
ASSERT_EQ(frame1.allocated_size(kVPlane), frame2.allocated_size(kVPlane));
EXPECT_EQ(0,
memcmp(frame1.buffer(kVPlane),
frame2.buffer(kVPlane),
frame1.allocated_size(kVPlane)));
}
void ExpectEqualFramesVector(const std::vector<I420VideoFrame*>& frames1,
const std::vector<I420VideoFrame*>& frames2) {
EXPECT_EQ(frames1.size(), frames2.size());
for (size_t i = 0; i < std::min(frames1.size(), frames2.size()); ++i)
ExpectEqualFrames(*frames1[i], *frames2[i]);
}
I420VideoFrame* CreateI420VideoFrame(int width, int height, uint8_t data) {
I420VideoFrame* frame = new I420VideoFrame();
const int kSizeY = width * height * 2;
const int kSizeUV = width * height;
scoped_ptr<uint8_t[]> buffer(new uint8_t[kSizeY]);
memset(buffer.get(), data, kSizeY);
frame->CreateFrame(kSizeY,
buffer.get(),
kSizeUV,
buffer.get(),
kSizeUV,
buffer.get(),
width,
height,
width,
width / 2,
width / 2);
frame->set_timestamp(data);
frame->set_ntp_time_ms(data);
frame->set_render_time_ms(data);
return frame;
}
} // namespace webrtc

View File

@ -487,10 +487,6 @@ void ViEEncoder::DeliverFrame(int id,
}
encoder_paused_and_dropped_frame_ = false;
}
if (video_frame->native_handle() != NULL) {
// TODO(wuchengli): add texture support. http://crbug.com/362437
return;
}
// Convert render time, in ms, to RTP timestamp.
const int kMsToRtpTimestamp = 90;
@ -501,22 +497,6 @@ void ViEEncoder::DeliverFrame(int id,
TRACE_EVENT_ASYNC_STEP0("webrtc", "Video", video_frame->render_time_ms(),
"Encode");
video_frame->set_timestamp(time_stamp);
{
CriticalSectionScoped cs(callback_cs_.get());
if (effect_filter_) {
unsigned int length = CalcBufferSize(kI420,
video_frame->width(),
video_frame->height());
scoped_ptr<uint8_t[]> video_buffer(new uint8_t[length]);
ExtractBuffer(*video_frame, length, video_buffer.get());
effect_filter_->Transform(length,
video_buffer.get(),
video_frame->ntp_time_ms(),
video_frame->timestamp(),
video_frame->width(),
video_frame->height());
}
}
// Make sure the CSRC list is correct.
if (num_csrcs > 0) {
@ -530,17 +510,37 @@ void ViEEncoder::DeliverFrame(int id,
}
default_rtp_rtcp_->SetCSRCs(tempCSRC, (uint8_t) num_csrcs);
}
// Pass frame via preprocessor.
I420VideoFrame* decimated_frame = NULL;
const int ret = vpm_.PreprocessFrame(*video_frame, &decimated_frame);
if (ret == 1) {
// Drop this frame.
return;
// TODO(wuchengli): support texture frames.
if (video_frame->native_handle() == NULL) {
{
CriticalSectionScoped cs(callback_cs_.get());
if (effect_filter_) {
unsigned int length =
CalcBufferSize(kI420, video_frame->width(), video_frame->height());
scoped_ptr<uint8_t[]> video_buffer(new uint8_t[length]);
ExtractBuffer(*video_frame, length, video_buffer.get());
effect_filter_->Transform(length,
video_buffer.get(),
video_frame->ntp_time_ms(),
video_frame->timestamp(),
video_frame->width(),
video_frame->height());
}
}
// Pass frame via preprocessor.
const int ret = vpm_.PreprocessFrame(*video_frame, &decimated_frame);
if (ret == 1) {
// Drop this frame.
return;
}
if (ret != VPM_OK) {
return;
}
}
if (ret != VPM_OK) {
return;
}
// Frame was not sampled => use original.
// If the frame was not resampled or scaled => use original.
if (decimated_frame == NULL) {
decimated_frame = video_frame;
}
@ -551,6 +551,11 @@ void ViEEncoder::DeliverFrame(int id,
pre_encode_callback_->FrameCallback(decimated_frame);
}
if (video_frame->native_handle() != NULL) {
// TODO(wuchengli): add texture support. http://crbug.com/362437
return;
}
#ifdef VIDEOCODEC_VP8
if (vcm_.SendCodec() == webrtc::kVideoCodecVP8) {
webrtc::CodecSpecificInfo codec_specific_info;

View File

@ -55,18 +55,22 @@ void ViEFrameProviderBase::DeliverFrame(
// Deliver the frame to all registered callbacks.
if (frame_callbacks_.size() > 0) {
if (frame_callbacks_.size() == 1 || video_frame->native_handle() != NULL) {
if (frame_callbacks_.size() == 1) {
// We don't have to copy the frame.
frame_callbacks_.front()->DeliverFrame(id_, video_frame, num_csrcs, CSRC);
} else {
// Make a copy of the frame for all callbacks.callback
for (FrameCallbacks::iterator it = frame_callbacks_.begin();
it != frame_callbacks_.end(); ++it) {
if (!extra_frame_.get()) {
extra_frame_.reset(new I420VideoFrame());
if (video_frame->native_handle() != NULL) {
(*it)->DeliverFrame(id_, video_frame, num_csrcs, CSRC);
} else {
// Make a copy of the frame for all callbacks.
if (!extra_frame_.get()) {
extra_frame_.reset(new I420VideoFrame());
}
extra_frame_->CopyFrame(*video_frame);
(*it)->DeliverFrame(id_, extra_frame_.get(), num_csrcs, CSRC);
}
extra_frame_->CopyFrame(*video_frame);
(*it)->DeliverFrame(id_, extra_frame_.get(), num_csrcs, CSRC);
}
}
}