1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/gtest/TestVideoTrackEncoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,300 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 +* License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "gtest/gtest.h" 1.9 +#include <algorithm> 1.10 + 1.11 +#include "mozilla/ArrayUtils.h" 1.12 +#include "VP8TrackEncoder.h" 1.13 +#include "ImageContainer.h" 1.14 +#include "MediaStreamGraph.h" 1.15 +#include "WebMWriter.h" // TODO: it's weird to include muxer header to get the class definition of VP8 METADATA 1.16 + 1.17 +using ::testing::TestWithParam; 1.18 +using ::testing::Values; 1.19 + 1.20 +using namespace mozilla::layers; 1.21 +using namespace mozilla; 1.22 + 1.23 +// A helper object to generate of different YUV planes. 1.24 +class YUVBufferGenerator { 1.25 +public: 1.26 + YUVBufferGenerator() {} 1.27 + 1.28 + void Init(const mozilla::gfx::IntSize &aSize) 1.29 + { 1.30 + mImageSize = aSize; 1.31 + 1.32 + int yPlaneLen = aSize.width * aSize.height; 1.33 + int cbcrPlaneLen = (yPlaneLen + 1) / 2; 1.34 + int frameLen = yPlaneLen + cbcrPlaneLen; 1.35 + 1.36 + // Generate source buffer. 1.37 + mSourceBuffer.SetLength(frameLen); 1.38 + 1.39 + // Fill Y plane. 1.40 + memset(mSourceBuffer.Elements(), 0x10, yPlaneLen); 1.41 + 1.42 + // Fill Cb/Cr planes. 1.43 + memset(mSourceBuffer.Elements() + yPlaneLen, 0x80, cbcrPlaneLen); 1.44 + } 1.45 + 1.46 + mozilla::gfx::IntSize GetSize() const 1.47 + { 1.48 + return mImageSize; 1.49 + } 1.50 + 1.51 + void Generate(nsTArray<nsRefPtr<Image> > &aImages) 1.52 + { 1.53 + aImages.AppendElement(CreateI420Image()); 1.54 + aImages.AppendElement(CreateNV12Image()); 1.55 + aImages.AppendElement(CreateNV21Image()); 1.56 + } 1.57 + 1.58 +private: 1.59 + Image *CreateI420Image() 1.60 + { 1.61 + PlanarYCbCrImage *image = new PlanarYCbCrImage(new BufferRecycleBin()); 1.62 + PlanarYCbCrData data; 1.63 + 1.64 + const uint32_t yPlaneSize = mImageSize.width * mImageSize.height; 1.65 + const uint32_t halfWidth = (mImageSize.width + 1) / 2; 1.66 + const uint32_t halfHeight = (mImageSize.height + 1) / 2; 1.67 + const uint32_t uvPlaneSize = halfWidth * halfHeight; 1.68 + 1.69 + // Y plane. 1.70 + uint8_t *y = mSourceBuffer.Elements(); 1.71 + data.mYChannel = y; 1.72 + data.mYSize.width = mImageSize.width; 1.73 + data.mYSize.height = mImageSize.height; 1.74 + data.mYStride = mImageSize.width; 1.75 + data.mYSkip = 0; 1.76 + 1.77 + // Cr plane. 1.78 + uint8_t *cr = y + yPlaneSize + uvPlaneSize; 1.79 + data.mCrChannel = cr; 1.80 + data.mCrSkip = 0; 1.81 + 1.82 + // Cb plane 1.83 + uint8_t *cb = y + yPlaneSize; 1.84 + data.mCbChannel = cb; 1.85 + data.mCbSkip = 0; 1.86 + 1.87 + // CrCb plane vectors. 1.88 + data.mCbCrStride = halfWidth; 1.89 + data.mCbCrSize.width = halfWidth; 1.90 + data.mCbCrSize.height = halfHeight; 1.91 + 1.92 + image->SetData(data); 1.93 + return image; 1.94 + } 1.95 + 1.96 + Image *CreateNV12Image() 1.97 + { 1.98 + PlanarYCbCrImage *image = new PlanarYCbCrImage(new BufferRecycleBin()); 1.99 + PlanarYCbCrData data; 1.100 + 1.101 + const uint32_t yPlaneSize = mImageSize.width * mImageSize.height; 1.102 + const uint32_t halfWidth = (mImageSize.width + 1) / 2; 1.103 + const uint32_t halfHeight = (mImageSize.height + 1) / 2; 1.104 + 1.105 + // Y plane. 1.106 + uint8_t *y = mSourceBuffer.Elements(); 1.107 + data.mYChannel = y; 1.108 + data.mYSize.width = mImageSize.width; 1.109 + data.mYSize.height = mImageSize.height; 1.110 + data.mYStride = mImageSize.width; 1.111 + data.mYSkip = 0; 1.112 + 1.113 + // Cr plane. 1.114 + uint8_t *cr = y + yPlaneSize; 1.115 + data.mCrChannel = cr; 1.116 + data.mCrSkip = 1; 1.117 + 1.118 + // Cb plane 1.119 + uint8_t *cb = y + yPlaneSize + 1; 1.120 + data.mCbChannel = cb; 1.121 + data.mCbSkip = 1; 1.122 + 1.123 + // 4:2:0. 1.124 + data.mCbCrStride = mImageSize.width; 1.125 + data.mCbCrSize.width = halfWidth; 1.126 + data.mCbCrSize.height = halfHeight; 1.127 + 1.128 + image->SetData(data); 1.129 + return image; 1.130 + } 1.131 + 1.132 + Image *CreateNV21Image() 1.133 + { 1.134 + PlanarYCbCrImage *image = new PlanarYCbCrImage(new BufferRecycleBin()); 1.135 + PlanarYCbCrData data; 1.136 + 1.137 + const uint32_t yPlaneSize = mImageSize.width * mImageSize.height; 1.138 + const uint32_t halfWidth = (mImageSize.width + 1) / 2; 1.139 + const uint32_t halfHeight = (mImageSize.height + 1) / 2; 1.140 + 1.141 + // Y plane. 1.142 + uint8_t *y = mSourceBuffer.Elements(); 1.143 + data.mYChannel = y; 1.144 + data.mYSize.width = mImageSize.width; 1.145 + data.mYSize.height = mImageSize.height; 1.146 + data.mYStride = mImageSize.width; 1.147 + data.mYSkip = 0; 1.148 + 1.149 + // Cr plane. 1.150 + uint8_t *cr = y + yPlaneSize + 1; 1.151 + data.mCrChannel = cr; 1.152 + data.mCrSkip = 1; 1.153 + 1.154 + // Cb plane 1.155 + uint8_t *cb = y + yPlaneSize; 1.156 + data.mCbChannel = cb; 1.157 + data.mCbSkip = 1; 1.158 + 1.159 + // 4:2:0. 1.160 + data.mCbCrStride = mImageSize.width; 1.161 + data.mCbCrSize.width = halfWidth; 1.162 + data.mCbCrSize.height = halfHeight; 1.163 + 1.164 + image->SetData(data); 1.165 + return image; 1.166 + } 1.167 + 1.168 +private: 1.169 + mozilla::gfx::IntSize mImageSize; 1.170 + nsTArray<uint8_t> mSourceBuffer; 1.171 +}; 1.172 + 1.173 +struct InitParam { 1.174 + bool mShouldSucceed; // This parameter should cause success or fail result 1.175 + int mWidth; // frame width 1.176 + int mHeight; // frame height 1.177 + mozilla::TrackRate mTrackRate; // track rate. 90K is the most commond track rate. 1.178 +}; 1.179 + 1.180 +class TestVP8TrackEncoder: public VP8TrackEncoder 1.181 +{ 1.182 +public: 1.183 + ::testing::AssertionResult TestInit(const InitParam &aParam) 1.184 + { 1.185 + nsresult result = Init(aParam.mWidth, aParam.mHeight, aParam.mWidth, aParam.mHeight, aParam.mTrackRate); 1.186 + 1.187 + if (((NS_FAILED(result) && aParam.mShouldSucceed)) || (NS_SUCCEEDED(result) && !aParam.mShouldSucceed)) 1.188 + { 1.189 + return ::testing::AssertionFailure() 1.190 + << " width = " << aParam.mWidth 1.191 + << " height = " << aParam.mHeight 1.192 + << " TrackRate = " << aParam.mTrackRate << "."; 1.193 + } 1.194 + else 1.195 + { 1.196 + return ::testing::AssertionSuccess(); 1.197 + } 1.198 + } 1.199 +}; 1.200 + 1.201 +// Init test 1.202 +TEST(VP8VideoTrackEncoder, Initialization) 1.203 +{ 1.204 + InitParam params[] = { 1.205 + // Failure cases. 1.206 + { false, 640, 480, 0 }, // Trackrate should be larger than 1. 1.207 + { false, 640, 480, -1 }, // Trackrate should be larger than 1. 1.208 + { false, 0, 0, 90000 }, // Height/ width should be larger than 1. 1.209 + { false, 0, 1, 90000 }, // Height/ width should be larger than 1. 1.210 + { false, 1, 0, 90000}, // Height/ width should be larger than 1. 1.211 + 1.212 + // Success cases 1.213 + { true, 640, 480, 90000}, // Standard VGA 1.214 + { true, 800, 480, 90000}, // Standard WVGA 1.215 + { true, 960, 540, 90000}, // Standard qHD 1.216 + { true, 1280, 720, 90000} // Standard HD 1.217 + }; 1.218 + 1.219 + for (size_t i = 0; i < ArrayLength(params); i++) 1.220 + { 1.221 + TestVP8TrackEncoder encoder; 1.222 + EXPECT_TRUE(encoder.TestInit(params[i])); 1.223 + } 1.224 +} 1.225 + 1.226 +// Get MetaData test 1.227 +TEST(VP8VideoTrackEncoder, FetchMetaData) 1.228 +{ 1.229 + InitParam params[] = { 1.230 + // Success cases 1.231 + { true, 640, 480, 90000}, // Standard VGA 1.232 + { true, 800, 480, 90000}, // Standard WVGA 1.233 + { true, 960, 540, 90000}, // Standard qHD 1.234 + { true, 1280, 720, 90000} // Standard HD 1.235 + }; 1.236 + 1.237 + for (size_t i = 0; i < ArrayLength(params); i++) 1.238 + { 1.239 + TestVP8TrackEncoder encoder; 1.240 + EXPECT_TRUE(encoder.TestInit(params[i])); 1.241 + 1.242 + nsRefPtr<TrackMetadataBase> meta = encoder.GetMetadata(); 1.243 + nsRefPtr<VP8Metadata> vp8Meta(static_cast<VP8Metadata*>(meta.get())); 1.244 + 1.245 + // METADATA should be depend on how to initiate encoder. 1.246 + EXPECT_TRUE(vp8Meta->mWidth == params[i].mWidth); 1.247 + EXPECT_TRUE(vp8Meta->mHeight == params[i].mHeight); 1.248 + } 1.249 +} 1.250 + 1.251 +// Encode test 1.252 +// XXX(bug 1018402): Disable this test when compiled with VS2013 because it 1.253 +// crashes. 1.254 +#if !defined(_MSC_VER) || _MSC_VER < 1800 1.255 +TEST(VP8VideoTrackEncoder, FrameEncode) 1.256 +{ 1.257 + // Initiate VP8 encoder 1.258 + TestVP8TrackEncoder encoder; 1.259 + InitParam param = {true, 640, 480, 90000}; 1.260 + encoder.TestInit(param); 1.261 + 1.262 + // Create YUV images as source. 1.263 + nsTArray<nsRefPtr<Image>> images; 1.264 + YUVBufferGenerator generator; 1.265 + generator.Init(mozilla::gfx::IntSize(640, 480)); 1.266 + generator.Generate(images); 1.267 + 1.268 + // Put generated YUV frame into video segment. 1.269 + // Duration of each frame is 1 second. 1.270 + VideoSegment segment; 1.271 + for (nsTArray<nsRefPtr<Image>>::size_type i = 0; i < images.Length(); i++) 1.272 + { 1.273 + nsRefPtr<Image> image = images[i]; 1.274 + segment.AppendFrame(image.forget(), mozilla::TrackTicks(90000), generator.GetSize()); 1.275 + } 1.276 + 1.277 + // track change notification. 1.278 + encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, 0, 0, segment); 1.279 + 1.280 + // Pull Encoded Data back from encoder. 1.281 + EncodedFrameContainer container; 1.282 + EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container))); 1.283 +} 1.284 +#endif // _MSC_VER 1.285 + 1.286 +// EOS test 1.287 +TEST(VP8VideoTrackEncoder, EncodeComplete) 1.288 +{ 1.289 + // Initiate VP8 encoder 1.290 + TestVP8TrackEncoder encoder; 1.291 + InitParam param = {true, 640, 480, 90000}; 1.292 + encoder.TestInit(param); 1.293 + 1.294 + // track end notification. 1.295 + VideoSegment segment; 1.296 + encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, 0, MediaStreamListener::TRACK_EVENT_ENDED, segment); 1.297 + 1.298 + // Pull Encoded Data back from encoder. Since we have sent 1.299 + // EOS to encoder, encoder.GetEncodedTrack should return 1.300 + // NS_OK immidiately. 1.301 + EncodedFrameContainer container; 1.302 + EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container))); 1.303 +}