Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |