// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // Tests PPB_MediaStreamVideoTrack interface. #include "ppapi/tests/test_media_stream_video_track.h" #include "ppapi/c/private/ppb_testing_private.h" #include "ppapi/cpp/completion_callback.h" #include "ppapi/cpp/instance.h" #include "ppapi/cpp/var.h" #include "ppapi/cpp/video_frame.h" #include "ppapi/tests/test_utils.h" #include "ppapi/tests/testing_instance.h" REGISTER_TEST_CASE(MediaStreamVideoTrack); namespace { const int32_t kTimes = 3; const int32_t kDefaultWidth = 640; const int32_t kDefaultHeight = 480; const char kJSCode[] = "function gotStream(stream) {" " var track = stream.getVideoTracks()[0];" " var plugin = document.getElementById('plugin');" " plugin.postMessage(track);" "}" "var constraints = {" " audio: false," " video: { mandatory: { minWidth: 640, minHeight: 480 } }" "};" "navigator.getUserMedia =" " navigator.getUserMedia || navigator.webkitGetUserMedia;" "navigator.getUserMedia(constraints," " gotStream, function() {});"; } TestMediaStreamVideoTrack::TestMediaStreamVideoTrack(TestingInstance* instance) : TestCase(instance), event_(instance_->pp_instance()) { } bool TestMediaStreamVideoTrack::Init() { return true; } TestMediaStreamVideoTrack::~TestMediaStreamVideoTrack() { } void TestMediaStreamVideoTrack::RunTests(const std::string& filter) { RUN_TEST(Create, filter); RUN_TEST(GetFrame, filter); RUN_TEST(Configure, filter); } void TestMediaStreamVideoTrack::HandleMessage(const pp::Var& message) { if (message.is_resource()) video_track_ = pp::MediaStreamVideoTrack(message.AsResource()); event_.Signal(); } std::string TestMediaStreamVideoTrack::TestCreate() { // Create a track. instance_->EvalScript(kJSCode); event_.Wait(); event_.Reset(); ASSERT_FALSE(video_track_.is_null()); ASSERT_FALSE(video_track_.HasEnded()); ASSERT_FALSE(video_track_.GetId().empty()); // Close the track. video_track_.Close(); ASSERT_TRUE(video_track_.HasEnded()); video_track_ = pp::MediaStreamVideoTrack(); PASS(); } std::string TestMediaStreamVideoTrack::TestGetFrame() { // Create a track. instance_->EvalScript(kJSCode); event_.Wait(); event_.Reset(); ASSERT_FALSE(video_track_.is_null()); ASSERT_FALSE(video_track_.HasEnded()); ASSERT_FALSE(video_track_.GetId().empty()); PP_TimeDelta timestamp = 0.0; // Get |kTimes| frames. for (int i = 0; i < kTimes; ++i) { TestCompletionCallbackWithOutput cc( instance_->pp_instance(), false); cc.WaitForResult(video_track_.GetFrame(cc.GetCallback())); ASSERT_EQ(PP_OK, cc.result()); pp::VideoFrame frame = cc.output(); ASSERT_FALSE(frame.is_null()); ASSERT_TRUE(frame.GetFormat() == PP_VIDEOFRAME_FORMAT_YV12 || frame.GetFormat() == PP_VIDEOFRAME_FORMAT_I420); pp::Size size; ASSERT_TRUE(frame.GetSize(&size)); ASSERT_EQ(size.width(), kDefaultWidth); ASSERT_EQ(size.height(), kDefaultHeight); ASSERT_GE(frame.GetTimestamp(), timestamp); timestamp = frame.GetTimestamp(); ASSERT_GT(frame.GetDataBufferSize(), 0U); ASSERT_TRUE(frame.GetDataBuffer() != NULL); video_track_.RecycleFrame(frame); // A recycled frame should be invalidated. ASSERT_EQ(frame.GetFormat(), PP_VIDEOFRAME_FORMAT_UNKNOWN); ASSERT_FALSE(frame.GetSize(&size)); ASSERT_EQ(frame.GetDataBufferSize(), 0U); ASSERT_TRUE(frame.GetDataBuffer() == NULL); } // Close the track. video_track_.Close(); ASSERT_TRUE(video_track_.HasEnded()); video_track_ = pp::MediaStreamVideoTrack(); PASS(); } std::string TestMediaStreamVideoTrack::TestConfigure() { // Create a track. instance_->EvalScript(kJSCode); event_.Wait(); event_.Reset(); ASSERT_FALSE(video_track_.is_null()); ASSERT_FALSE(video_track_.HasEnded()); ASSERT_FALSE(video_track_.GetId().empty()); // Configure format. struct { int32_t format; int32_t expected_format; } formats[] = { { PP_VIDEOFRAME_FORMAT_BGRA, PP_VIDEOFRAME_FORMAT_BGRA }, // To RGBA. { PP_VIDEOFRAME_FORMAT_I420, PP_VIDEOFRAME_FORMAT_I420 }, // To I420. { PP_VIDEOFRAME_FORMAT_YV12, PP_VIDEOFRAME_FORMAT_YV12 }, // To YV12. { PP_VIDEOFRAME_FORMAT_BGRA, PP_VIDEOFRAME_FORMAT_BGRA }, // To RGBA. { PP_VIDEOFRAME_FORMAT_UNKNOWN, PP_VIDEOFRAME_FORMAT_YV12 }, // To default. }; for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) { TestCompletionCallback cc1(instance_->pp_instance(), false); int32_t attrib_list[] = { PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT, formats[i].format, PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE, }; cc1.WaitForResult(video_track_.Configure(attrib_list, cc1.GetCallback())); ASSERT_EQ(PP_OK, cc1.result()); for (int j = 0; j < kTimes; ++j) { TestCompletionCallbackWithOutput cc2( instance_->pp_instance(), false); cc2.WaitForResult(video_track_.GetFrame(cc2.GetCallback())); ASSERT_EQ(PP_OK, cc2.result()); pp::VideoFrame frame = cc2.output(); ASSERT_FALSE(frame.is_null()); if (formats[i].format != PP_VIDEOFRAME_FORMAT_UNKNOWN) { ASSERT_EQ(frame.GetFormat(), formats[i].expected_format); } else { // Both YV12 and I420 are acceptable as default YUV formats. ASSERT_TRUE(frame.GetFormat() == PP_VIDEOFRAME_FORMAT_YV12 || frame.GetFormat() == PP_VIDEOFRAME_FORMAT_I420); } pp::Size size; ASSERT_TRUE(frame.GetSize(&size)); ASSERT_EQ(size.width(), kDefaultWidth); ASSERT_EQ(size.height(), kDefaultHeight); ASSERT_GT(frame.GetDataBufferSize(), 0U); ASSERT_TRUE(frame.GetDataBuffer() != NULL); video_track_.RecycleFrame(frame); } } // Configure size. struct { int32_t width; int32_t height; int32_t expect_width; int32_t expect_height; } sizes[] = { { 72, 72, 72, 72 }, // To 72x27. { 1024, 768, 1024, 768 }, // To 1024x768. { 0, 0, kDefaultWidth, kDefaultHeight }, // To default. }; for (size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) { TestCompletionCallback cc1(instance_->pp_instance(), false); int32_t attrib_list[] = { PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH, sizes[i].width, PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT, sizes[i].height, PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE, }; cc1.WaitForResult(video_track_.Configure(attrib_list, cc1.GetCallback())); ASSERT_EQ(PP_OK, cc1.result()); for (int j = 0; j < kTimes; ++j) { TestCompletionCallbackWithOutput cc2( instance_->pp_instance(), false); cc2.WaitForResult(video_track_.GetFrame(cc2.GetCallback())); ASSERT_EQ(PP_OK, cc2.result()); pp::VideoFrame frame = cc2.output(); ASSERT_FALSE(frame.is_null()); ASSERT_TRUE(frame.GetFormat() == PP_VIDEOFRAME_FORMAT_YV12 || frame.GetFormat() == PP_VIDEOFRAME_FORMAT_I420); pp::Size size; ASSERT_TRUE(frame.GetSize(&size)); ASSERT_EQ(size.width(), sizes[i].expect_width); ASSERT_EQ(size.height(), sizes[i].expect_height); ASSERT_GT(frame.GetDataBufferSize(), 0U); ASSERT_TRUE(frame.GetDataBuffer() != NULL); video_track_.RecycleFrame(frame); } } // Close the track. video_track_.Close(); ASSERT_TRUE(video_track_.HasEnded()); video_track_ = pp::MediaStreamVideoTrack(); PASS(); }