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 |