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::TrackId TrackId; |
| 16 typedef StreamParser::BufferQueue BufferQueue; |
| 17 typedef StreamParser::TextBufferQueueMap TextBufferQueueMap; |
| 18 |
| 19 const int kEnd = -1; |
| 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; |
| 25 |
| 26 static bool IsAudio(scoped_refptr<StreamParserBuffer> buffer) { |
| 27 return buffer->type() == DemuxerStream::AUDIO; |
| 28 } |
| 29 |
| 30 static bool IsVideo(scoped_refptr<StreamParserBuffer> buffer) { |
| 31 return buffer->type() == DemuxerStream::VIDEO; |
| 32 } |
| 33 |
| 34 static bool IsText(scoped_refptr<StreamParserBuffer> buffer) { |
| 35 return buffer->type() == DemuxerStream::TEXT; |
| 36 } |
| 37 |
| 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, |
| 45 TrackId track_id, |
| 46 BufferQueue* queue) { |
| 47 DCHECK(decode_timestamps); |
| 48 DCHECK(queue); |
| 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 base::TimeDelta::FromMicroseconds(decode_timestamps[i])); |
| 57 queue->push_back(buffer); |
| 58 } |
| 59 } |
| 60 |
| 61 class StreamParserTest : public testing::Test { |
| 62 protected: |
| 63 StreamParserTest() {} |
| 64 |
| 65 // Returns the number of buffers in |merged_buffers_| for which |predicate| |
| 66 // returns true. |
| 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(), |
| 71 predicate)); |
| 72 } |
| 73 |
| 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, |
| 78 &audio_buffers_); |
| 79 } |
| 80 |
| 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, |
| 85 &video_buffers_); |
| 86 } |
| 87 |
| 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 |
| 95 // value. |
| 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, |
| 100 &text_buffers_a_); |
| 101 text_map_.insert(std::make_pair(kTextTrackIdA, text_buffers_a_)); |
| 102 } |
| 103 |
| 104 if (decode_timestamps_b) { |
| 105 GenerateBuffers(decode_timestamps_b, DemuxerStream::TEXT, kTextTrackIdB, |
| 106 &text_buffers_b_); |
| 107 text_map_.insert(std::make_pair(kTextTrackIdB, 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) the buffer's track_id() |
| 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 |
| 144 break; |
| 145 default: |
| 146 NOTREACHED(); |
| 147 } |
| 148 results_stream << buffer.track_id() << ":"; |
| 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_; |
| 228 BufferQueue video_buffers_; |
| 229 BufferQueue text_buffers_a_; |
| 230 BufferQueue text_buffers_b_; |
| 231 BufferQueue merged_buffers_; |
| 232 TextBufferQueueMap text_map_; |
| 233 |
| 234 DISALLOW_COPY_AND_ASSIGN(StreamParserTest); |
| 235 }; |
| 236 |
| 237 TEST_F(StreamParserTest, MergeBufferQueues_AllEmpty) { |
| 238 std::string expected = ""; |
| 239 VerifyMergeSuccess(expected, true); |
| 240 } |
| 241 |
| 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); |
| 247 } |
| 248 |
| 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); |
| 254 } |
| 255 |
| 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); |
| 261 } |
| 262 |
| 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); |
| 270 } |
| 271 |
| 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); |
| 278 } |
| 279 |
| 280 TEST_F(StreamParserTest, MergeBufferQueues_OverlappingAudioVideoText) { |
| 281 std::string expected = "A0:100 V1:101 T2:102 V1:103 T3:104 A0:105 V1:106 " |
| 282 "T2:107"; |
| 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); |
| 291 } |
| 292 |
| 293 TEST_F(StreamParserTest, MergeBufferQueues_NonDecreasingNoCrossMediaDuplicate) { |
| 294 std::string expected = "A0:100 A0:100 A0:100 V1:101 V1:101 V1:101 A0:102 " |
| 295 "V1:103 V1:103"; |
| 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); |
| 301 } |
| 302 |
| 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); |
| 315 } |
| 316 |
| 317 TEST_F(StreamParserTest, MergeBufferQueues_InvalidDecreasingSingleStream) { |
| 318 int audio_timestamps[] = { 101, 102, 100, 103, kEnd }; |
| 319 GenerateAudioBuffers(audio_timestamps); |
| 320 VerifyMergeFailure(); |
| 321 } |
| 322 |
| 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(); |
| 329 } |
| 330 |
| 331 TEST_F(StreamParserTest, MergeBufferQueues_ValidAppendToExistingMerge) { |
| 332 std::string expected = "A0:100 V1:101 T2:102 V1:103 T3:104 A0:105 V1:106 " |
| 333 "T2:107"; |
| 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); |
| 342 |
| 343 ClearQueuesAndTextMapButKeepAnyMergedBuffers(); |
| 344 |
| 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); |
| 355 } |
| 356 |
| 357 TEST_F(StreamParserTest, MergeBufferQueues_InvalidAppendToExistingMerge) { |
| 358 std::string expected = "A0:100 V1:101 T2:102 V1:103 T3:104 A0:105 V1:106 " |
| 359 "T2:107"; |
| 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); |
| 368 |
| 369 // Appending empty buffers to pre-existing merge result should succeed and not |
| 370 // change the existing result. |
| 371 ClearQueuesAndTextMapButKeepAnyMergedBuffers(); |
| 372 VerifyMergeSuccess(expected, true); |
| 373 |
| 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(); |
| 379 } |
| 380 |
| 381 } // namespace media |
| 382 |
OLD | NEW |