Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1110)

Side by Side Diff: media/base/stream_parser_unittest.cc

Issue 149153002: MSE: Add StreamParser buffer remuxing utility and tests (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased and addressed comments from PS2 Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 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 <algorithm>
6 #include <sstream>
7
8 #include "base/basictypes.h"
9 #include "media/base/stream_parser.h"
10 #include "media/base/stream_parser_buffer.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 namespace media {
14
15 typedef StreamParser::BufferQueue BufferQueue;
16 typedef StreamParser::TextBufferQueueMap TextBufferQueueMap;
17
18 const int kEnd = -1;
19 const uint8 kFakeData[] = { 0xFF };
20
21 static bool IsAudio(scoped_refptr<StreamParserBuffer> buffer) {
22 return buffer->type() == DemuxerStream::AUDIO;
23 }
24
25 static bool IsVideo(scoped_refptr<StreamParserBuffer> buffer) {
26 return buffer->type() == DemuxerStream::VIDEO;
27 }
28
29 static bool IsText(scoped_refptr<StreamParserBuffer> buffer) {
30 return buffer->type() == DemuxerStream::TEXT;
31 }
32
33 // Creates and appends a sequence of StreamParserBuffers to the provided
34 // |queue|. |decode_timestamps| determines the number of appended buffers and
35 // their sequence of decode timestamps; a |kEnd| timestamp indicates the
36 // end of the sequence and no buffer is appended for it. Each new buffer's
37 // type will be |type|, and if text, its text track number will be set to
38 // |text_track_number|.
39 static void GenerateBuffers(const int* decode_timestamps,
40 StreamParserBuffer::Type type,
41 int text_track_number,
42 BufferQueue* queue) {
43 DCHECK(decode_timestamps);
44 DCHECK(queue);
45 DCHECK_NE(type, DemuxerStream::UNKNOWN);
46 DCHECK_LT(type, DemuxerStream::NUM_TYPES);
47 for (int i = 0; decode_timestamps[i] != kEnd; ++i) {
48 scoped_refptr<StreamParserBuffer> buffer =
49 StreamParserBuffer::CopyFrom(kFakeData, sizeof(kFakeData),
50 true, type);
51 if (type == DemuxerStream::TEXT)
52 buffer->set_text_track_number(text_track_number);
53 buffer->SetDecodeTimestamp(
54 base::TimeDelta::FromMicroseconds(decode_timestamps[i]));
55 queue->push_back(buffer);
56 }
57 }
58
59 class StreamParserTest : public testing::Test {
60 protected:
61 StreamParserTest() {}
62
63 // Returns the number of buffers in |merged_buffers_| for which |predicate|
64 // returns true.
65 size_t CountMatchingMergedBuffers(
66 bool (*predicate)(scoped_refptr<StreamParserBuffer> buffer)) {
67 return static_cast<size_t>(count_if(merged_buffers_.begin(),
68 merged_buffers_.end(),
69 predicate));
70 }
71
72 // Appends test audio buffers in the sequence described by |decode_timestamps|
73 // to |audio_buffers_|. See GenerateBuffers() for |decode_timestamps| format.
74 void GenerateAudioBuffers(const int* decode_timestamps) {
75 GenerateBuffers(decode_timestamps, DemuxerStream::AUDIO, -1,
76 &audio_buffers_);
77 }
78
79 // Appends test video buffers in the sequence described by |decode_timestamps|
80 // to |video_buffers_|. See GenerateBuffers() for |decode_timestamps| format.
81 void GenerateVideoBuffers(const int* decode_timestamps) {
82 GenerateBuffers(decode_timestamps, DemuxerStream::VIDEO, -1,
83 &video_buffers_);
84 }
85
86 // Current tests only need up to two distinct text BufferQueues. This helper
87 // conditionally appends buffers to the underlying |text_buffers_a_| and
88 // |text_buffers_b_| and conditionally inserts these BufferQueues into
89 // |text_map_| keyed by the respective |text_track_number_{a,b}|. If
90 // |decode_timestamps_{a,b}| is NULL, then the corresponding BufferQueue is
91 // neither appended to nor inserted into |text_map_| (though it may previously
92 // have been inserted under either the same or different key). Note that
93 // key collision on map insertion does not replace the previous value.
94 void GenerateTextBuffers(const int* decode_timestamps_a,
95 int text_track_number_a,
96 const int* decode_timestamps_b,
97 int text_track_number_b) {
98 if (decode_timestamps_a) {
99 GenerateBuffers(decode_timestamps_a, DemuxerStream::TEXT,
100 text_track_number_a, &text_buffers_a_);
101 text_map_.insert(std::make_pair(text_track_number_a, text_buffers_a_));
102 }
103
104 if (decode_timestamps_b) {
105 GenerateBuffers(decode_timestamps_b, DemuxerStream::TEXT,
106 text_track_number_b, &text_buffers_b_);
107 text_map_.insert(std::make_pair(text_track_number_b, text_buffers_b_));
108 }
109 }
110
111 // Returns a string that describes the sequence of buffers in
112 // |merged_buffers_|. The string is a concatenation of space-delimited buffer
113 // descriptors in the same sequence as |merged_buffers_|. Each descriptor is
114 // the concatenation of
115 // 1) a single character that describes the buffer's type(), e.g. A, V, or T
116 // for audio, video, or text, respectively
117 // 2) if text, the buffer's text_track_number(), otherwise nothing
118 // 3) ":"
119 // 4) the buffer's decode timestamp.
120 // If |include_type_and_text_track| is false, then items 1, 2, and 3 are
121 // not included in descriptors. This is useful when buffers with different
122 // media types but the same decode timestamp are expected, and the exact
123 // sequence of media types for the tying timestamps is not subject to
124 // verification.
125 std::string MergedBufferQueueString(bool include_type_and_text_track) {
126 std::stringstream results_stream;
127 for (BufferQueue::const_iterator itr = merged_buffers_.begin();
128 itr != merged_buffers_.end();
129 ++itr) {
130 if (itr != merged_buffers_.begin())
131 results_stream << " ";
132 const StreamParserBuffer& buffer = *(*itr);
133 if (include_type_and_text_track) {
134 switch (buffer.type()) {
135 case DemuxerStream::AUDIO:
136 results_stream << "A";
137 break;
138 case DemuxerStream::VIDEO:
139 results_stream << "V";
140 break;
141 case DemuxerStream::TEXT:
142 results_stream << "T";
143 results_stream << buffer.text_track_number();
144 break;
145 default:
146 NOTREACHED();
147 }
148 results_stream << ":";
149 }
150 results_stream << buffer.GetDecodeTimestamp().InMicroseconds();
151 }
152
153 return results_stream.str();
154 }
155
156 // Verifies that MergeBufferQueues() of the current |audio_buffers_|,
157 // |video_buffers_|, |text_map_|, and |merged_buffers_| returns true and
158 // results in an updated |merged_buffers_| that matches expectation. The
159 // expectation, specified in |expected|, is compared to the string resulting
160 // from MergedBufferQueueString() (see comments for that method) with
161 // |verify_type_and_text_track_sequence| passed. |merged_buffers_| is appended
162 // to by the merge, and may be setup by the caller to have some pre-existing
163 // buffers; it is both an input and output of this method.
164 // Regardless of |verify_type_and_text_track_sequence|, the marginal number
165 // of buffers of each type (audio, video, text) resulting from the merge is
166 // also verified to match the number of buffers in |audio_buffers_|,
167 // |video_buffers_|, and |text_map_|, respectively.
168 void VerifyMergeSuccess(const std::string& expected,
169 bool verify_type_and_text_track_sequence) {
170 // |merged_buffers| may already have some buffers. Count them by type for
171 // later inclusion in verification.
172 size_t original_audio_in_merged = CountMatchingMergedBuffers(IsAudio);
173 size_t original_video_in_merged = CountMatchingMergedBuffers(IsVideo);
174 size_t original_text_in_merged = CountMatchingMergedBuffers(IsText);
175
176 EXPECT_TRUE(MergeBufferQueues(audio_buffers_, video_buffers_, text_map_,
177 &merged_buffers_));
178
179 // Verify resulting contents of |merged_buffers| matches |expected|.
180 EXPECT_EQ(expected,
181 MergedBufferQueueString(verify_type_and_text_track_sequence));
182
183 // Verify that the correct number of each type of buffer is in the merge
184 // result.
185 size_t audio_in_merged = CountMatchingMergedBuffers(IsAudio);
186 size_t video_in_merged = CountMatchingMergedBuffers(IsVideo);
187 size_t text_in_merged = CountMatchingMergedBuffers(IsText);
188
189 EXPECT_GE(audio_in_merged, original_audio_in_merged);
190 EXPECT_GE(video_in_merged, original_video_in_merged);
191 EXPECT_GE(text_in_merged, original_text_in_merged);
192
193 EXPECT_EQ(audio_buffers_.size(),
194 audio_in_merged - original_audio_in_merged);
195 EXPECT_EQ(video_buffers_.size(),
196 video_in_merged - original_video_in_merged);
197
198 size_t expected_text_buffer_count = 0;
199 for (TextBufferQueueMap::const_iterator itr = text_map_.begin();
200 itr != text_map_.end();
201 ++itr) {
202 expected_text_buffer_count += itr->second.size();
203 }
204 EXPECT_EQ(expected_text_buffer_count,
205 text_in_merged - original_text_in_merged);
206 }
207
208 // Verifies that MergeBufferQueues() of the current |audio_buffers_|,
209 // |video_buffers_|, |text_map_|, and |merged_buffers_| returns false.
210 void VerifyMergeFailure() {
211 EXPECT_FALSE(MergeBufferQueues(audio_buffers_, video_buffers_, text_map_,
212 &merged_buffers_));
213 }
214
215 // Helper to allow tests to clear all the input BufferQueues (except
216 // |merged_buffers_|) and the TextBufferQueueMap that are used in
217 // VerifyMerge{Success/Failure}().
218 void ClearQueuesAndTextMapButKeepAnyMergedBuffers() {
219 audio_buffers_.clear();
220 video_buffers_.clear();
221 text_buffers_a_.clear();
222 text_buffers_b_.clear();
223 text_map_.clear();
224 }
225
226 private:
227 BufferQueue audio_buffers_, video_buffers_, text_buffers_a_, text_buffers_b_;
xhwang 2014/02/06 00:36:32 I don't find a rule against this, but I rarely see
wolenetz 2014/02/06 23:56:03 Done.
228 BufferQueue merged_buffers_;
229 TextBufferQueueMap text_map_;
230
231 DISALLOW_COPY_AND_ASSIGN(StreamParserTest);
232 };
233
234 TEST_F(StreamParserTest, MergeBufferQueues_AllEmpty) {
235 std::string expected = "";
236 VerifyMergeSuccess(expected, true);
237 }
238
239 TEST_F(StreamParserTest, MergeBufferQueues_SingleAudioBuffer) {
240 std::string expected = "A:100";
241 int audio_timestamps[] = { 100, kEnd };
242 GenerateAudioBuffers(audio_timestamps);
243 VerifyMergeSuccess(expected, true);
244 }
245
246 TEST_F(StreamParserTest, MergeBufferQueues_SingleVideoBuffer) {
247 std::string expected = "V:100";
248 int video_timestamps[] = { 100, kEnd };
249 GenerateVideoBuffers(video_timestamps);
250 VerifyMergeSuccess(expected, true);
251 }
252
253 TEST_F(StreamParserTest, MergeBufferQueues_SingleTextBuffer) {
254 std::string expected = "T12:100";
255 int text_timestamps[] = { 100, kEnd };
256 GenerateTextBuffers(text_timestamps, 12, NULL, -1);
257 VerifyMergeSuccess(expected, true);
258 }
259
260 TEST_F(StreamParserTest, MergeBufferQueues_OverlappingAudioVideo) {
261 std::string expected = "A:100 V:101 V:102 A:103 A:104 V:105";
262 int audio_timestamps[] = { 100, 103, 104, kEnd };
263 GenerateAudioBuffers(audio_timestamps);
264 int video_timestamps[] = { 101, 102, 105, kEnd };
265 GenerateVideoBuffers(video_timestamps);
266 VerifyMergeSuccess(expected, true);
267 }
268
269 TEST_F(StreamParserTest, MergeBufferQueues_OverlappingMultipleText) {
270 std::string expected = "T1:100 T1:101 T2:103 T1:104 T2:105 T2:106";
271 int text_timestamps_1[] = { 100, 101, 104, kEnd };
272 int text_timestamps_2[] = { 103, 105, 106, kEnd };
273 GenerateTextBuffers(text_timestamps_1, 1, text_timestamps_2, 2);
274 VerifyMergeSuccess(expected, true);
275 }
276
277 TEST_F(StreamParserTest, MergeBufferQueues_OverlappingAudioVideoText) {
278 std::string expected = "A:100 V:101 T1:102 V:103 T2:104 A:105 V:106 T1:107";
279 int audio_timestamps[] = { 100, 105, kEnd };
280 GenerateAudioBuffers(audio_timestamps);
281 int video_timestamps[] = { 101, 103, 106, kEnd };
282 GenerateVideoBuffers(video_timestamps);
283 int text_timestamps_1[] = { 102, 107, kEnd };
284 int text_timestamps_2[] = { 104, kEnd };
285 GenerateTextBuffers(text_timestamps_1, 1, text_timestamps_2, 2);
286 VerifyMergeSuccess(expected, true);
287 }
288
289 TEST_F(StreamParserTest, MergeBufferQueues_NonDecreasingNoCrossMediaDuplicate) {
290 std::string expected = "A:100 A:100 A:100 V:101 V:101 V:101 A:102 V:103 "
291 "V:103";
292 int audio_timestamps[] = { 100, 100, 100, 102, kEnd };
293 GenerateAudioBuffers(audio_timestamps);
294 int video_timestamps[] = { 101, 101, 101, 103, 103, kEnd };
295 GenerateVideoBuffers(video_timestamps);
296 VerifyMergeSuccess(expected, true);
297 }
298
299 TEST_F(StreamParserTest, MergeBufferQueues_CrossStreamDuplicates) {
300 // Interface keeps the choice undefined of which stream's buffer wins the
301 // selection when timestamps are tied. Verify at least the right number of
302 // each kind of buffer results, and that buffers are in nondecreasing order.
303 std::string expected = "100 100 100 100 100 100 102 102 102 102 102 102 102";
304 int audio_timestamps[] = { 100, 100, 100, 102, kEnd };
305 GenerateAudioBuffers(audio_timestamps);
306 int video_timestamps[] = { 100, 100, 102, 102, 102, kEnd };
307 GenerateVideoBuffers(video_timestamps);
308 int text_timestamps[] = { 100, 102, 102, 102, kEnd };
309 GenerateTextBuffers(text_timestamps, 1, NULL, -1);
310 VerifyMergeSuccess(expected, false);
311 }
312
313 TEST_F(StreamParserTest, MergeBufferQueues_InvalidDecreasingSingleStream) {
314 int audio_timestamps[] = { 101, 102, 100, 103, kEnd };
315 GenerateAudioBuffers(audio_timestamps);
316 VerifyMergeFailure();
317 }
318
319 TEST_F(StreamParserTest, MergeBufferQueues_InvalidDecreasingMultipleStreams) {
320 int audio_timestamps[] = { 101, 102, 100, 103, kEnd };
321 GenerateAudioBuffers(audio_timestamps);
322 int video_timestamps[] = { 104, 100, kEnd };
323 GenerateVideoBuffers(video_timestamps);
324 VerifyMergeFailure();
325 }
326
327 TEST_F(StreamParserTest, MergeBufferQueues_ValidAppendToExistingMerge) {
328 std::string expected = "A:100 V:101 T1:102 V:103 T2:104 A:105 V:106 T1:107";
329 int audio_timestamps[] = { 100, 105, kEnd };
330 GenerateAudioBuffers(audio_timestamps);
331 int video_timestamps[] = { 101, 103, 106, kEnd };
332 GenerateVideoBuffers(video_timestamps);
333 int text_timestamps_1[] = { 102, 107, kEnd };
334 int text_timestamps_2[] = { 104, kEnd };
335 GenerateTextBuffers(text_timestamps_1, 1, text_timestamps_2, 2);
336 VerifyMergeSuccess(expected, true);
337
338 ClearQueuesAndTextMapButKeepAnyMergedBuffers();
339
340 expected = "A:100 V:101 T1:102 V:103 T2:104 A:105 V:106 T1:107 "
341 "A:107 V:111 T1:112 V:113 T2:114 A:115 V:116 T1:117";
342 int more_audio_timestamps[] = { 107, 115, kEnd };
343 GenerateAudioBuffers(more_audio_timestamps);
344 int more_video_timestamps[] = { 111, 113, 116, kEnd };
345 GenerateVideoBuffers(more_video_timestamps);
346 int more_text_timestamps_1[] = { 112, 117, kEnd };
347 int more_text_timestamps_2[] = { 114, kEnd };
348 GenerateTextBuffers(more_text_timestamps_1, 1, more_text_timestamps_2, 2);
349 VerifyMergeSuccess(expected, true);
350 }
351
352 TEST_F(StreamParserTest, MergeBufferQueues_InvalidAppendToExistingMerge) {
353 std::string expected = "A:100 V:101 T1:102 V:103 T2:104 A:105 V:106 T1:107";
354 int audio_timestamps[] = { 100, 105, kEnd };
355 GenerateAudioBuffers(audio_timestamps);
356 int video_timestamps[] = { 101, 103, 106, kEnd };
357 GenerateVideoBuffers(video_timestamps);
358 int text_timestamps_1[] = { 102, 107, kEnd };
359 int text_timestamps_2[] = { 104, kEnd };
360 GenerateTextBuffers(text_timestamps_1, 1, text_timestamps_2, 2);
361 VerifyMergeSuccess(expected, true);
362
363 // Appending empty buffers to pre-existing merge result should succeed and not
364 // change the existing result.
365 ClearQueuesAndTextMapButKeepAnyMergedBuffers();
366 VerifyMergeSuccess(expected, true);
367
368 // But appending something with a lower timestamp than the last timestamp
369 // in the pre-existing merge result should fail.
370 int more_audio_timestamps[] = { 106, kEnd };
371 GenerateAudioBuffers(more_audio_timestamps);
372 VerifyMergeFailure();
373 }
374
375 } // namespace media
376
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698