| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <stddef.h> | |
| 6 #include <stdint.h> | |
| 7 | |
| 8 #include "base/bind.h" | |
| 9 #include "base/location.h" | |
| 10 #include "base/macros.h" | |
| 11 #include "base/memory/ref_counted.h" | |
| 12 #include "base/memory/scoped_ptr.h" | |
| 13 #include "media/audio/audio_parameters.h" | |
| 14 #include "media/base/channel_layout.h" | |
| 15 #include "media/base/video_frame.h" | |
| 16 #include "media/capture/webm_muxer.h" | |
| 17 #include "testing/gmock/include/gmock/gmock.h" | |
| 18 #include "testing/gtest/include/gtest/gtest.h" | |
| 19 | |
| 20 using ::testing::_; | |
| 21 using ::testing::AllOf; | |
| 22 using ::testing::AnyNumber; | |
| 23 using ::testing::AtLeast; | |
| 24 using ::testing::Eq; | |
| 25 using ::testing::InSequence; | |
| 26 using ::testing::Mock; | |
| 27 using ::testing::Not; | |
| 28 using ::testing::Sequence; | |
| 29 using ::testing::TestWithParam; | |
| 30 using ::testing::ValuesIn; | |
| 31 using ::testing::WithArgs; | |
| 32 | |
| 33 namespace media { | |
| 34 | |
| 35 struct kTestParams { | |
| 36 VideoCodec codec; | |
| 37 size_t num_video_tracks; | |
| 38 size_t num_audio_tracks; | |
| 39 }; | |
| 40 | |
| 41 class WebmMuxerTest : public TestWithParam<kTestParams> { | |
| 42 public: | |
| 43 WebmMuxerTest() | |
| 44 : webm_muxer_(GetParam().codec, | |
| 45 GetParam().num_video_tracks, | |
| 46 GetParam().num_audio_tracks, | |
| 47 base::Bind(&WebmMuxerTest::WriteCallback, | |
| 48 base::Unretained(this))), | |
| 49 last_encoded_length_(0), | |
| 50 accumulated_position_(0) { | |
| 51 EXPECT_EQ(webm_muxer_.Position(), 0); | |
| 52 const mkvmuxer::int64 kRandomNewPosition = 333; | |
| 53 EXPECT_EQ(webm_muxer_.Position(kRandomNewPosition), -1); | |
| 54 EXPECT_FALSE(webm_muxer_.Seekable()); | |
| 55 } | |
| 56 | |
| 57 MOCK_METHOD1(WriteCallback, void(base::StringPiece)); | |
| 58 | |
| 59 void SaveEncodedDataLen(const base::StringPiece& encoded_data) { | |
| 60 last_encoded_length_ = encoded_data.size(); | |
| 61 accumulated_position_ += encoded_data.size(); | |
| 62 } | |
| 63 | |
| 64 mkvmuxer::int64 GetWebmMuxerPosition() const { | |
| 65 return webm_muxer_.Position(); | |
| 66 } | |
| 67 | |
| 68 mkvmuxer::Segment::Mode GetWebmSegmentMode() const { | |
| 69 return webm_muxer_.segment_.mode(); | |
| 70 } | |
| 71 | |
| 72 mkvmuxer::int32 WebmMuxerWrite(const void* buf, mkvmuxer::uint32 len) { | |
| 73 return webm_muxer_.Write(buf, len); | |
| 74 } | |
| 75 | |
| 76 WebmMuxer webm_muxer_; | |
| 77 | |
| 78 size_t last_encoded_length_; | |
| 79 int64_t accumulated_position_; | |
| 80 | |
| 81 private: | |
| 82 DISALLOW_COPY_AND_ASSIGN(WebmMuxerTest); | |
| 83 }; | |
| 84 | |
| 85 // Checks that the WriteCallback is called with appropriate params when | |
| 86 // WebmMuxer::Write() method is called. | |
| 87 TEST_P(WebmMuxerTest, Write) { | |
| 88 const base::StringPiece encoded_data("abcdefghijklmnopqrstuvwxyz"); | |
| 89 | |
| 90 EXPECT_CALL(*this, WriteCallback(encoded_data)); | |
| 91 WebmMuxerWrite(encoded_data.data(), encoded_data.size()); | |
| 92 | |
| 93 EXPECT_EQ(GetWebmMuxerPosition(), static_cast<int64_t>(encoded_data.size())); | |
| 94 } | |
| 95 | |
| 96 // This test sends two frames and checks that the WriteCallback is called with | |
| 97 // appropriate params in both cases. | |
| 98 TEST_P(WebmMuxerTest, OnEncodedVideoTwoFrames) { | |
| 99 if (GetParam().num_audio_tracks > 0) | |
| 100 return; | |
| 101 | |
| 102 const gfx::Size frame_size(160, 80); | |
| 103 const scoped_refptr<VideoFrame> video_frame = | |
| 104 VideoFrame::CreateBlackFrame(frame_size); | |
| 105 const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); | |
| 106 | |
| 107 EXPECT_CALL(*this, WriteCallback(_)) | |
| 108 .Times(AtLeast(1)) | |
| 109 .WillRepeatedly( | |
| 110 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); | |
| 111 webm_muxer_.OnEncodedVideo(video_frame, | |
| 112 make_scoped_ptr(new std::string(encoded_data)), | |
| 113 base::TimeTicks::Now(), | |
| 114 false /* keyframe */); | |
| 115 | |
| 116 // First time around WriteCallback() is pinged a number of times to write the | |
| 117 // Matroska header, but at the end it dumps |encoded_data|. | |
| 118 EXPECT_EQ(last_encoded_length_, encoded_data.size()); | |
| 119 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); | |
| 120 EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); | |
| 121 EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); | |
| 122 | |
| 123 const int64_t begin_of_second_block = accumulated_position_; | |
| 124 EXPECT_CALL(*this, WriteCallback(_)) | |
| 125 .Times(AtLeast(1)) | |
| 126 .WillRepeatedly( | |
| 127 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); | |
| 128 webm_muxer_.OnEncodedVideo(video_frame, | |
| 129 make_scoped_ptr(new std::string(encoded_data)), | |
| 130 base::TimeTicks::Now(), | |
| 131 false /* keyframe */); | |
| 132 | |
| 133 // The second time around the callbacks should include a SimpleBlock header, | |
| 134 // namely the track index, a timestamp and a flags byte, for a total of 6B. | |
| 135 EXPECT_EQ(last_encoded_length_, encoded_data.size()); | |
| 136 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); | |
| 137 const uint32_t kSimpleBlockSize = 6u; | |
| 138 EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + | |
| 139 encoded_data.size()), | |
| 140 accumulated_position_); | |
| 141 } | |
| 142 | |
| 143 TEST_P(WebmMuxerTest, OnEncodedAudioTwoFrames) { | |
| 144 if (GetParam().num_video_tracks > 0) | |
| 145 return; | |
| 146 | |
| 147 const int sample_rate = 48000; | |
| 148 const int bits_per_sample = 16; | |
| 149 const int frames_per_buffer = 480; | |
| 150 media::AudioParameters audio_params( | |
| 151 media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, | |
| 152 media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, | |
| 153 frames_per_buffer); | |
| 154 | |
| 155 const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); | |
| 156 | |
| 157 EXPECT_CALL(*this, WriteCallback(_)) | |
| 158 .Times(AtLeast(1)) | |
| 159 .WillRepeatedly( | |
| 160 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); | |
| 161 webm_muxer_.OnEncodedAudio(audio_params, | |
| 162 make_scoped_ptr(new std::string(encoded_data)), | |
| 163 base::TimeTicks::Now()); | |
| 164 | |
| 165 // First time around WriteCallback() is pinged a number of times to write the | |
| 166 // Matroska header, but at the end it dumps |encoded_data|. | |
| 167 EXPECT_EQ(last_encoded_length_, encoded_data.size()); | |
| 168 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); | |
| 169 EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); | |
| 170 EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); | |
| 171 | |
| 172 const int64_t begin_of_second_block = accumulated_position_; | |
| 173 EXPECT_CALL(*this, WriteCallback(_)) | |
| 174 .Times(AtLeast(1)) | |
| 175 .WillRepeatedly( | |
| 176 WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); | |
| 177 webm_muxer_.OnEncodedAudio(audio_params, | |
| 178 make_scoped_ptr(new std::string(encoded_data)), | |
| 179 base::TimeTicks::Now()); | |
| 180 | |
| 181 // The second time around the callbacks should include a SimpleBlock header, | |
| 182 // namely the track index, a timestamp and a flags byte, for a total of 6B. | |
| 183 EXPECT_EQ(last_encoded_length_, encoded_data.size()); | |
| 184 EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); | |
| 185 const uint32_t kSimpleBlockSize = 6u; | |
| 186 EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + | |
| 187 encoded_data.size()), | |
| 188 accumulated_position_); | |
| 189 } | |
| 190 | |
| 191 // This test verifies that when video data comes before audio data, we save the | |
| 192 // encoded video frames and add it to the video track when audio data arrives. | |
| 193 TEST_P(WebmMuxerTest, VideoIsStoredWhileWaitingForAudio) { | |
| 194 // This test is only relevant if we have both kinds of tracks. | |
| 195 if (GetParam().num_video_tracks == 0 || GetParam().num_audio_tracks == 0) | |
| 196 return; | |
| 197 | |
| 198 // First send a video keyframe | |
| 199 const gfx::Size frame_size(160, 80); | |
| 200 const scoped_refptr<VideoFrame> video_frame = | |
| 201 VideoFrame::CreateBlackFrame(frame_size); | |
| 202 const std::string encoded_video("thisisanencodedvideopacket"); | |
| 203 webm_muxer_.OnEncodedVideo(video_frame, | |
| 204 make_scoped_ptr(new std::string(encoded_video)), | |
| 205 base::TimeTicks::Now(), true /* keyframe */); | |
| 206 // A few encoded non key frames. | |
| 207 const int kNumNonKeyFrames = 2; | |
| 208 for (int i = 0; i < kNumNonKeyFrames; ++i) { | |
| 209 webm_muxer_.OnEncodedVideo(video_frame, | |
| 210 make_scoped_ptr(new std::string(encoded_video)), | |
| 211 base::TimeTicks::Now(), false /* keyframe */); | |
| 212 } | |
| 213 | |
| 214 // Send some audio. The header will be written and muxing will proceed | |
| 215 // normally. | |
| 216 const int sample_rate = 48000; | |
| 217 const int bits_per_sample = 16; | |
| 218 const int frames_per_buffer = 480; | |
| 219 media::AudioParameters audio_params( | |
| 220 media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, | |
| 221 media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, | |
| 222 frames_per_buffer); | |
| 223 const std::string encoded_audio("thisisanencodedaudiopacket"); | |
| 224 | |
| 225 // We should first get the encoded video frames, then the encoded audio frame. | |
| 226 Sequence s; | |
| 227 EXPECT_CALL(*this, WriteCallback(Eq(encoded_video))) | |
| 228 .Times(1 + kNumNonKeyFrames) | |
| 229 .InSequence(s); | |
| 230 EXPECT_CALL(*this, WriteCallback(Eq(encoded_audio))).Times(1).InSequence(s); | |
| 231 // We'll also get lots of other header-related stuff. | |
| 232 EXPECT_CALL(*this, WriteCallback( | |
| 233 AllOf(Not(Eq(encoded_video)), Not(Eq(encoded_audio))))) | |
| 234 .Times(AnyNumber()); | |
| 235 webm_muxer_.OnEncodedAudio(audio_params, | |
| 236 make_scoped_ptr(new std::string(encoded_audio)), | |
| 237 base::TimeTicks::Now()); | |
| 238 } | |
| 239 | |
| 240 const kTestParams kTestCases[] = { | |
| 241 // TODO: consider not enumerating every combination by hand. | |
| 242 {kCodecVP8, 1 /* num_video_tracks */, 0 /*num_audio_tracks*/}, | |
| 243 {kCodecVP8, 0, 1}, | |
| 244 {kCodecVP8, 1, 1}, | |
| 245 {kCodecVP9, 1, 0}, | |
| 246 {kCodecVP9, 0, 1}, | |
| 247 {kCodecVP9, 1, 1}, | |
| 248 }; | |
| 249 | |
| 250 INSTANTIATE_TEST_CASE_P(, WebmMuxerTest, ValuesIn(kTestCases)); | |
| 251 | |
| 252 } // namespace media | |
| OLD | NEW |