| 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 "media/base/android/access_unit_queue.h" |
| 6 #include "testing/gtest/include/gtest/gtest.h" |
| 7 |
| 8 #define ARRAY_SIZE(x) (int)(sizeof(x)/sizeof(x[0])) |
| 9 |
| 10 namespace media { |
| 11 |
| 12 class AccessUnitQueueTest : public testing::Test { |
| 13 public: |
| 14 AccessUnitQueueTest() {} |
| 15 ~AccessUnitQueueTest() override {} |
| 16 |
| 17 protected: |
| 18 enum Flags {kNone = 0, kKeyFrame = 1, kEOS = 2, kConfig = 4}; |
| 19 struct AUDescriptor { |
| 20 int flags; |
| 21 std::string data; |
| 22 }; |
| 23 |
| 24 DemuxerData CreateDemuxerData(const AUDescriptor* descr, int descr_length); |
| 25 }; |
| 26 |
| 27 DemuxerData AccessUnitQueueTest::CreateDemuxerData( |
| 28 const AUDescriptor* descr, int descr_length) { |
| 29 DemuxerData result; |
| 30 result.type = DemuxerStream::AUDIO; // assign a valid type |
| 31 |
| 32 for (int i = 0; i < descr_length; ++i) { |
| 33 result.access_units.push_back(AccessUnit()); |
| 34 AccessUnit& au = result.access_units.back(); |
| 35 au.status = (descr[i].flags & kConfig) ? |
| 36 DemuxerStream::kConfigChanged : DemuxerStream::kOk; |
| 37 if (au.status == DemuxerStream::kConfigChanged) { |
| 38 result.demuxer_configs.push_back(DemuxerConfigs()); |
| 39 continue; |
| 40 } |
| 41 |
| 42 au.data = std::vector<uint8>(descr[i].data.begin(), descr[i].data.end()); |
| 43 au.is_key_frame = (descr[i].flags & kKeyFrame); |
| 44 au.is_end_of_stream = (descr[i].flags & kEOS); |
| 45 } |
| 46 return result; |
| 47 } |
| 48 |
| 49 #define VERIFY_FIRST_BYTE(expected, info) \ |
| 50 do { \ |
| 51 EXPECT_NE(nullptr, info.front_unit); \ |
| 52 EXPECT_TRUE(info.front_unit->data.size() > 0); \ |
| 53 EXPECT_EQ(expected, info.front_unit->data[0]); \ |
| 54 } \ |
| 55 while (0) |
| 56 |
| 57 TEST_F(AccessUnitQueueTest, InitializedEmpty) { |
| 58 AccessUnitQueue au_queue; |
| 59 AccessUnitQueue::Info info; |
| 60 au_queue.GetInfo(&info); |
| 61 |
| 62 EXPECT_EQ(0, info.length); |
| 63 EXPECT_FALSE(info.has_eos); |
| 64 EXPECT_EQ(nullptr, info.front_unit); |
| 65 EXPECT_EQ(nullptr, info.configs); |
| 66 } |
| 67 |
| 68 TEST_F(AccessUnitQueueTest, SkipToKeyFrameEmptyQueue) { |
| 69 AccessUnitQueue au_queue; |
| 70 EXPECT_FALSE(au_queue.SkipToKeyFrame()); |
| 71 } |
| 72 |
| 73 TEST_F(AccessUnitQueueTest, PushAndAdvance) { |
| 74 AUDescriptor chunk[] = { |
| 75 {kNone, "0"}, |
| 76 {kNone, "1"}, |
| 77 {kNone, "2"}, |
| 78 {kNone, "3"} |
| 79 }; |
| 80 |
| 81 int chunk_size = ARRAY_SIZE(chunk); |
| 82 |
| 83 AccessUnitQueue au_queue; |
| 84 au_queue.PushBack(CreateDemuxerData(chunk, chunk_size)); |
| 85 |
| 86 AccessUnitQueue::Info info; |
| 87 for (int i = 0; i < chunk_size; ++i) { |
| 88 au_queue.GetInfo(&info); |
| 89 |
| 90 EXPECT_FALSE(info.has_eos); |
| 91 EXPECT_EQ(chunk_size - i, info.length); |
| 92 EXPECT_EQ(nullptr, info.configs); |
| 93 |
| 94 EXPECT_NE(nullptr, info.front_unit); |
| 95 EXPECT_TRUE(info.front_unit->data.size() > 0); |
| 96 EXPECT_EQ('0' + i, info.front_unit->data[0]); |
| 97 |
| 98 au_queue.Advance(); |
| 99 } |
| 100 |
| 101 // After we advanced past the last AU, GetInfo() should report starvation. |
| 102 au_queue.GetInfo(&info); |
| 103 |
| 104 EXPECT_EQ(0, info.length); |
| 105 EXPECT_FALSE(info.has_eos); |
| 106 EXPECT_EQ(nullptr, info.front_unit); |
| 107 EXPECT_EQ(nullptr, info.configs); |
| 108 } |
| 109 |
| 110 TEST_F(AccessUnitQueueTest, ChunksDoNotLeak) { |
| 111 AUDescriptor chunk[] = { |
| 112 {kNone, "0"}, |
| 113 {kNone, "1"}, |
| 114 {kNone, "2"}, |
| 115 {kNone, "3"} |
| 116 }; |
| 117 |
| 118 AccessUnitQueue au_queue; |
| 119 au_queue.PushBack(CreateDemuxerData(chunk, ARRAY_SIZE(chunk))); |
| 120 |
| 121 // Verify that the old chunks get deleted (we rely on NumChunksForTesting()) |
| 122 for (int i = 0; i < 100; ++i) { |
| 123 au_queue.PushBack(CreateDemuxerData(chunk, ARRAY_SIZE(chunk))); |
| 124 for (int j = 0; j < ARRAY_SIZE(chunk); ++j) |
| 125 au_queue.Advance(); |
| 126 |
| 127 // 5 is an arbitrary number, it implies that we keep 4 chunks of history. |
| 128 EXPECT_GT(5U, au_queue.NumChunksForTesting()); |
| 129 } |
| 130 } |
| 131 |
| 132 TEST_F(AccessUnitQueueTest, PushAfterStarvation) { |
| 133 // Two chunks |
| 134 AUDescriptor chunk[][4] = {{ |
| 135 {kNone, "0"}, |
| 136 {kNone, "1"}, |
| 137 {kNone, "2"}, |
| 138 {kNone, "3"} |
| 139 }, { |
| 140 {kNone, "4"}, |
| 141 {kNone, "5"}, |
| 142 {kNone, "6"}, |
| 143 {kNone, "7"} |
| 144 } |
| 145 }; |
| 146 |
| 147 AccessUnitQueue au_queue; |
| 148 |
| 149 // Push the first chunk. |
| 150 au_queue.PushBack(CreateDemuxerData(chunk[0], ARRAY_SIZE(chunk[0]))); |
| 151 |
| 152 // Advance past the end of queue. |
| 153 for (int i = 0; i < ARRAY_SIZE(chunk[0]); ++i) |
| 154 au_queue.Advance(); |
| 155 |
| 156 // An extra Advance() should not change anything. |
| 157 au_queue.Advance(); |
| 158 |
| 159 // Push the second chunk |
| 160 au_queue.PushBack(CreateDemuxerData(chunk[1], ARRAY_SIZE(chunk[1]))); |
| 161 |
| 162 // Verify that we get the next access unit. |
| 163 AccessUnitQueue::Info info; |
| 164 au_queue.GetInfo(&info); |
| 165 VERIFY_FIRST_BYTE('4', info); |
| 166 } |
| 167 |
| 168 TEST_F(AccessUnitQueueTest, HasEOS) { |
| 169 // Two chunks |
| 170 AUDescriptor chunk[][4] = {{ |
| 171 {kNone, "0"}, |
| 172 {kNone, "1"}, |
| 173 {kNone, "2"}, |
| 174 {kNone, "3"} |
| 175 }, { |
| 176 {kNone, "4"}, |
| 177 {kNone, "5"}, |
| 178 {kNone, "6"}, |
| 179 {kEOS, "7"} |
| 180 } |
| 181 }; |
| 182 |
| 183 AccessUnitQueue au_queue; |
| 184 au_queue.PushBack(CreateDemuxerData(chunk[0], ARRAY_SIZE(chunk[0]))); |
| 185 au_queue.PushBack(CreateDemuxerData(chunk[1], ARRAY_SIZE(chunk[1]))); |
| 186 |
| 187 // Verify that after EOS has been pushed into the queue, |
| 188 // it is reported for every GetInfo() |
| 189 AccessUnitQueue::Info info; |
| 190 for (int i = 0; i < 8; ++i) { |
| 191 au_queue.GetInfo(&info); |
| 192 |
| 193 EXPECT_TRUE(info.has_eos); |
| 194 EXPECT_EQ(nullptr, info.configs); |
| 195 |
| 196 VERIFY_FIRST_BYTE('0' + i, info); |
| 197 |
| 198 au_queue.Advance(); |
| 199 } |
| 200 } |
| 201 |
| 202 TEST_F(AccessUnitQueueTest, HasConfigs) { |
| 203 AUDescriptor chunk[] = { |
| 204 {kNone, "0"}, |
| 205 {kNone, "1"}, |
| 206 {kNone, "2"}, |
| 207 {kConfig, "3"} |
| 208 }; |
| 209 |
| 210 AccessUnitQueue au_queue; |
| 211 au_queue.PushBack(CreateDemuxerData(chunk, ARRAY_SIZE(chunk))); |
| 212 |
| 213 AccessUnitQueue::Info info; |
| 214 for (int i = 0; i < 4; ++i) { |
| 215 au_queue.GetInfo(&info); |
| 216 |
| 217 if (i != 3) |
| 218 EXPECT_EQ(nullptr, info.configs); |
| 219 else |
| 220 EXPECT_NE(nullptr, info.configs); |
| 221 |
| 222 au_queue.Advance(); |
| 223 } |
| 224 } |
| 225 |
| 226 TEST_F(AccessUnitQueueTest, ConfigsAndKeyFrame) { |
| 227 // Two chunks |
| 228 AUDescriptor chunk[][4] = {{ |
| 229 {kNone, "0"}, |
| 230 {kKeyFrame, "1"}, |
| 231 {kNone, "2"}, |
| 232 {kConfig, "3"} |
| 233 }, { |
| 234 {kKeyFrame, "4"}, |
| 235 {kNone, "5"}, |
| 236 {kNone, "6"}, |
| 237 {kNone, "7"} |
| 238 } |
| 239 }; |
| 240 |
| 241 AccessUnitQueue::Info info; |
| 242 |
| 243 AccessUnitQueue au_queue; |
| 244 au_queue.PushBack(CreateDemuxerData(chunk[0], ARRAY_SIZE(chunk[0]))); |
| 245 au_queue.PushBack(CreateDemuxerData(chunk[1], ARRAY_SIZE(chunk[1]))); |
| 246 |
| 247 // There is no prior key frame |
| 248 EXPECT_FALSE(au_queue.SkipToKeyFrame()); |
| 249 |
| 250 // Consume first access unit. |
| 251 au_queue.Advance(); |
| 252 |
| 253 // Now the current one is the key frame. It would be safe to configure codec |
| 254 // at this moment, so SkipToKeyFrame() should return true. |
| 255 EXPECT_TRUE(au_queue.SkipToKeyFrame()); |
| 256 |
| 257 au_queue.GetInfo(&info); |
| 258 VERIFY_FIRST_BYTE('1', info); |
| 259 |
| 260 au_queue.Advance(); // now current unit is "2" |
| 261 |
| 262 au_queue.GetInfo(&info); |
| 263 VERIFY_FIRST_BYTE('2', info); |
| 264 |
| 265 EXPECT_TRUE(au_queue.SkipToKeyFrame()); // should go back to "1" |
| 266 |
| 267 au_queue.GetInfo(&info); |
| 268 VERIFY_FIRST_BYTE('1', info); |
| 269 |
| 270 au_queue.Advance(); // now current unit is "2" |
| 271 au_queue.Advance(); // now current unit is "3" |
| 272 |
| 273 // Verify that we are at "3". |
| 274 au_queue.GetInfo(&info); |
| 275 EXPECT_NE(nullptr, info.configs); |
| 276 |
| 277 // Although it would be safe to configure codec (with old config) in this |
| 278 // position since it will be immediately reconfigured from the next unit "3", |
| 279 // current implementation returns unit "1". |
| 280 |
| 281 EXPECT_TRUE(au_queue.SkipToKeyFrame()); // should go back to "1" |
| 282 |
| 283 au_queue.GetInfo(&info); |
| 284 VERIFY_FIRST_BYTE('1', info); |
| 285 |
| 286 au_queue.Advance(); // now current unit is "2" |
| 287 au_queue.Advance(); // now current unit is "3" |
| 288 au_queue.Advance(); // now current unit is "4" |
| 289 |
| 290 au_queue.GetInfo(&info); |
| 291 VERIFY_FIRST_BYTE('4', info); |
| 292 |
| 293 EXPECT_TRUE(au_queue.SkipToKeyFrame()); // should stay at "4" |
| 294 |
| 295 au_queue.GetInfo(&info); |
| 296 VERIFY_FIRST_BYTE('4', info); |
| 297 |
| 298 au_queue.Advance(); // now current unit is "5" |
| 299 au_queue.Advance(); // now current unit is "6" |
| 300 |
| 301 au_queue.GetInfo(&info); |
| 302 VERIFY_FIRST_BYTE('6', info); |
| 303 |
| 304 EXPECT_TRUE(au_queue.SkipToKeyFrame()); // should go back to "4" |
| 305 |
| 306 au_queue.GetInfo(&info); |
| 307 VERIFY_FIRST_BYTE('4', info); |
| 308 } |
| 309 |
| 310 } // namespace media |
| OLD | NEW |