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.
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"
15 typedef StreamParser::TrackId TrackId
;
16 typedef StreamParser::BufferQueue BufferQueue
;
17 typedef StreamParser::TextBufferQueueMap TextBufferQueueMap
;
20 const uint8 kFakeData
[] = { 0xFF };
21 const TrackId kAudioTrackId
= 0;
22 const TrackId kVideoTrackId
= 1;
23 const TrackId kTextTrackIdA
= 2;
24 const TrackId kTextTrackIdB
= 3;
26 static bool IsAudio(scoped_refptr
<StreamParserBuffer
> buffer
) {
27 return buffer
->type() == DemuxerStream::AUDIO
;
30 static bool IsVideo(scoped_refptr
<StreamParserBuffer
> buffer
) {
31 return buffer
->type() == DemuxerStream::VIDEO
;
34 static bool IsText(scoped_refptr
<StreamParserBuffer
> buffer
) {
35 return buffer
->type() == DemuxerStream::TEXT
;
38 // Creates and appends a sequence of StreamParserBuffers to the provided
39 // |queue|. |decode_timestamps| determines the number of appended buffers and
40 // their sequence of decode timestamps; a |kEnd| timestamp indicates the
41 // end of the sequence and no buffer is appended for it. Each new buffer's
42 // type will be |type| with track ID set to |track_id|.
43 static void GenerateBuffers(const int* decode_timestamps
,
44 StreamParserBuffer::Type type
,
47 DCHECK(decode_timestamps
);
49 DCHECK_NE(type
, DemuxerStream::UNKNOWN
);
50 DCHECK_LT(type
, DemuxerStream::NUM_TYPES
);
51 for (int i
= 0; decode_timestamps
[i
] != kEnd
; ++i
) {
52 scoped_refptr
<StreamParserBuffer
> buffer
=
53 StreamParserBuffer::CopyFrom(kFakeData
, sizeof(kFakeData
),
54 true, type
, track_id
);
55 buffer
->SetDecodeTimestamp(
56 DecodeTimestamp::FromMicroseconds(decode_timestamps
[i
]));
57 queue
->push_back(buffer
);
61 class StreamParserTest
: public testing::Test
{
65 // Returns the number of buffers in |merged_buffers_| for which |predicate|
67 size_t CountMatchingMergedBuffers(
68 bool (*predicate
)(scoped_refptr
<StreamParserBuffer
> buffer
)) {
69 return static_cast<size_t>(count_if(merged_buffers_
.begin(),
70 merged_buffers_
.end(),
74 // Appends test audio buffers in the sequence described by |decode_timestamps|
75 // to |audio_buffers_|. See GenerateBuffers() for |decode_timestamps| format.
76 void GenerateAudioBuffers(const int* decode_timestamps
) {
77 GenerateBuffers(decode_timestamps
, DemuxerStream::AUDIO
, kAudioTrackId
,
81 // Appends test video buffers in the sequence described by |decode_timestamps|
82 // to |video_buffers_|. See GenerateBuffers() for |decode_timestamps| format.
83 void GenerateVideoBuffers(const int* decode_timestamps
) {
84 GenerateBuffers(decode_timestamps
, DemuxerStream::VIDEO
, kVideoTrackId
,
88 // Current tests only need up to two distinct text BufferQueues. This helper
89 // conditionally appends buffers to the underlying |text_buffers_a_| and
90 // |text_buffers_b_| and conditionally inserts these BufferQueues into
91 // |text_map_| keyed by the respective track ID. If |decode_timestamps_{a,b}|
92 // is NULL, then the corresponding BufferQueue is neither appended to nor
93 // inserted into |text_map_| (though it may previously have been inserted).
94 // Note that key collision on map insertion does not replace the previous
96 void GenerateTextBuffers(const int* decode_timestamps_a
,
97 const int* decode_timestamps_b
) {
98 if (decode_timestamps_a
) {
99 GenerateBuffers(decode_timestamps_a
, DemuxerStream::TEXT
, kTextTrackIdA
,
101 text_map_
.insert(std::make_pair(kTextTrackIdA
, text_buffers_a_
));
104 if (decode_timestamps_b
) {
105 GenerateBuffers(decode_timestamps_b
, DemuxerStream::TEXT
, kTextTrackIdB
,
107 text_map_
.insert(std::make_pair(kTextTrackIdB
, text_buffers_b_
));
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) the buffer's track_id()
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
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();
130 if (itr
!= merged_buffers_
.begin())
131 results_stream
<< " ";
132 const StreamParserBuffer
& buffer
= *(itr
->get());
133 if (include_type_and_text_track
) {
134 switch (buffer
.type()) {
135 case DemuxerStream::AUDIO
:
136 results_stream
<< "A";
138 case DemuxerStream::VIDEO
:
139 results_stream
<< "V";
141 case DemuxerStream::TEXT
:
142 results_stream
<< "T";
148 results_stream
<< buffer
.track_id() << ":";
150 results_stream
<< buffer
.GetDecodeTimestamp().InMicroseconds();
153 return results_stream
.str();
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
);
176 EXPECT_TRUE(MergeBufferQueues(audio_buffers_
, video_buffers_
, text_map_
,
179 // Verify resulting contents of |merged_buffers| matches |expected|.
181 MergedBufferQueueString(verify_type_and_text_track_sequence
));
183 // Verify that the correct number of each type of buffer is in the merge
185 size_t audio_in_merged
= CountMatchingMergedBuffers(IsAudio
);
186 size_t video_in_merged
= CountMatchingMergedBuffers(IsVideo
);
187 size_t text_in_merged
= CountMatchingMergedBuffers(IsText
);
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
);
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
);
198 size_t expected_text_buffer_count
= 0;
199 for (TextBufferQueueMap::const_iterator itr
= text_map_
.begin();
200 itr
!= text_map_
.end();
202 expected_text_buffer_count
+= itr
->second
.size();
204 EXPECT_EQ(expected_text_buffer_count
,
205 text_in_merged
- original_text_in_merged
);
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_
,
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();
227 BufferQueue audio_buffers_
;
228 BufferQueue video_buffers_
;
229 BufferQueue text_buffers_a_
;
230 BufferQueue text_buffers_b_
;
231 BufferQueue merged_buffers_
;
232 TextBufferQueueMap text_map_
;
234 DISALLOW_COPY_AND_ASSIGN(StreamParserTest
);
237 TEST_F(StreamParserTest
, MergeBufferQueues_AllEmpty
) {
238 std::string expected
= "";
239 VerifyMergeSuccess(expected
, true);
242 TEST_F(StreamParserTest
, MergeBufferQueues_SingleAudioBuffer
) {
243 std::string expected
= "A0:100";
244 int audio_timestamps
[] = { 100, kEnd
};
245 GenerateAudioBuffers(audio_timestamps
);
246 VerifyMergeSuccess(expected
, true);
249 TEST_F(StreamParserTest
, MergeBufferQueues_SingleVideoBuffer
) {
250 std::string expected
= "V1:100";
251 int video_timestamps
[] = { 100, kEnd
};
252 GenerateVideoBuffers(video_timestamps
);
253 VerifyMergeSuccess(expected
, true);
256 TEST_F(StreamParserTest
, MergeBufferQueues_SingleTextBuffer
) {
257 std::string expected
= "T2:100";
258 int text_timestamps
[] = { 100, kEnd
};
259 GenerateTextBuffers(text_timestamps
, NULL
);
260 VerifyMergeSuccess(expected
, true);
263 TEST_F(StreamParserTest
, MergeBufferQueues_OverlappingAudioVideo
) {
264 std::string expected
= "A0:100 V1:101 V1:102 A0:103 A0:104 V1:105";
265 int audio_timestamps
[] = { 100, 103, 104, kEnd
};
266 GenerateAudioBuffers(audio_timestamps
);
267 int video_timestamps
[] = { 101, 102, 105, kEnd
};
268 GenerateVideoBuffers(video_timestamps
);
269 VerifyMergeSuccess(expected
, true);
272 TEST_F(StreamParserTest
, MergeBufferQueues_OverlappingMultipleText
) {
273 std::string expected
= "T2:100 T2:101 T3:103 T2:104 T3:105 T3:106";
274 int text_timestamps_a
[] = { 100, 101, 104, kEnd
};
275 int text_timestamps_b
[] = { 103, 105, 106, kEnd
};
276 GenerateTextBuffers(text_timestamps_a
, text_timestamps_b
);
277 VerifyMergeSuccess(expected
, true);
280 TEST_F(StreamParserTest
, MergeBufferQueues_OverlappingAudioVideoText
) {
281 std::string expected
= "A0:100 V1:101 T2:102 V1:103 T3:104 A0:105 V1:106 "
283 int audio_timestamps
[] = { 100, 105, kEnd
};
284 GenerateAudioBuffers(audio_timestamps
);
285 int video_timestamps
[] = { 101, 103, 106, kEnd
};
286 GenerateVideoBuffers(video_timestamps
);
287 int text_timestamps_a
[] = { 102, 107, kEnd
};
288 int text_timestamps_b
[] = { 104, kEnd
};
289 GenerateTextBuffers(text_timestamps_a
, text_timestamps_b
);
290 VerifyMergeSuccess(expected
, true);
293 TEST_F(StreamParserTest
, MergeBufferQueues_NonDecreasingNoCrossMediaDuplicate
) {
294 std::string expected
= "A0:100 A0:100 A0:100 V1:101 V1:101 V1:101 A0:102 "
296 int audio_timestamps
[] = { 100, 100, 100, 102, kEnd
};
297 GenerateAudioBuffers(audio_timestamps
);
298 int video_timestamps
[] = { 101, 101, 101, 103, 103, kEnd
};
299 GenerateVideoBuffers(video_timestamps
);
300 VerifyMergeSuccess(expected
, true);
303 TEST_F(StreamParserTest
, MergeBufferQueues_CrossStreamDuplicates
) {
304 // Interface keeps the choice undefined of which stream's buffer wins the
305 // selection when timestamps are tied. Verify at least the right number of
306 // each kind of buffer results, and that buffers are in nondecreasing order.
307 std::string expected
= "100 100 100 100 100 100 102 102 102 102 102 102 102";
308 int audio_timestamps
[] = { 100, 100, 100, 102, kEnd
};
309 GenerateAudioBuffers(audio_timestamps
);
310 int video_timestamps
[] = { 100, 100, 102, 102, 102, kEnd
};
311 GenerateVideoBuffers(video_timestamps
);
312 int text_timestamps
[] = { 100, 102, 102, 102, kEnd
};
313 GenerateTextBuffers(text_timestamps
, NULL
);
314 VerifyMergeSuccess(expected
, false);
317 TEST_F(StreamParserTest
, MergeBufferQueues_InvalidDecreasingSingleStream
) {
318 int audio_timestamps
[] = { 101, 102, 100, 103, kEnd
};
319 GenerateAudioBuffers(audio_timestamps
);
320 VerifyMergeFailure();
323 TEST_F(StreamParserTest
, MergeBufferQueues_InvalidDecreasingMultipleStreams
) {
324 int audio_timestamps
[] = { 101, 102, 100, 103, kEnd
};
325 GenerateAudioBuffers(audio_timestamps
);
326 int video_timestamps
[] = { 104, 100, kEnd
};
327 GenerateVideoBuffers(video_timestamps
);
328 VerifyMergeFailure();
331 TEST_F(StreamParserTest
, MergeBufferQueues_ValidAppendToExistingMerge
) {
332 std::string expected
= "A0:100 V1:101 T2:102 V1:103 T3:104 A0:105 V1:106 "
334 int audio_timestamps
[] = { 100, 105, kEnd
};
335 GenerateAudioBuffers(audio_timestamps
);
336 int video_timestamps
[] = { 101, 103, 106, kEnd
};
337 GenerateVideoBuffers(video_timestamps
);
338 int text_timestamps_a
[] = { 102, 107, kEnd
};
339 int text_timestamps_b
[] = { 104, kEnd
};
340 GenerateTextBuffers(text_timestamps_a
, text_timestamps_b
);
341 VerifyMergeSuccess(expected
, true);
343 ClearQueuesAndTextMapButKeepAnyMergedBuffers();
345 expected
= "A0:100 V1:101 T2:102 V1:103 T3:104 A0:105 V1:106 T2:107 "
346 "A0:107 V1:111 T2:112 V1:113 T3:114 A0:115 V1:116 T2:117";
347 int more_audio_timestamps
[] = { 107, 115, kEnd
};
348 GenerateAudioBuffers(more_audio_timestamps
);
349 int more_video_timestamps
[] = { 111, 113, 116, kEnd
};
350 GenerateVideoBuffers(more_video_timestamps
);
351 int more_text_timestamps_a
[] = { 112, 117, kEnd
};
352 int more_text_timestamps_b
[] = { 114, kEnd
};
353 GenerateTextBuffers(more_text_timestamps_a
, more_text_timestamps_b
);
354 VerifyMergeSuccess(expected
, true);
357 TEST_F(StreamParserTest
, MergeBufferQueues_InvalidAppendToExistingMerge
) {
358 std::string expected
= "A0:100 V1:101 T2:102 V1:103 T3:104 A0:105 V1:106 "
360 int audio_timestamps
[] = { 100, 105, kEnd
};
361 GenerateAudioBuffers(audio_timestamps
);
362 int video_timestamps
[] = { 101, 103, 106, kEnd
};
363 GenerateVideoBuffers(video_timestamps
);
364 int text_timestamps_a
[] = { 102, 107, kEnd
};
365 int text_timestamps_b
[] = { 104, kEnd
};
366 GenerateTextBuffers(text_timestamps_a
, text_timestamps_b
);
367 VerifyMergeSuccess(expected
, true);
369 // Appending empty buffers to pre-existing merge result should succeed and not
370 // change the existing result.
371 ClearQueuesAndTextMapButKeepAnyMergedBuffers();
372 VerifyMergeSuccess(expected
, true);
374 // But appending something with a lower timestamp than the last timestamp
375 // in the pre-existing merge result should fail.
376 int more_audio_timestamps
[] = { 106, kEnd
};
377 GenerateAudioBuffers(more_audio_timestamps
);
378 VerifyMergeFailure();