| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/base/android/test_data_factory.h" |
| |
| #include <iterator> |
| |
| #include "base/strings/stringprintf.h" |
| #include "media/base/android/demuxer_stream_player_params.h" |
| #include "media/base/decoder_buffer.h" |
| #include "media/base/test_data_util.h" |
| |
| namespace media { |
| |
| DemuxerConfigs TestDataFactory::CreateAudioConfigs(AudioCodec audio_codec, |
| base::TimeDelta duration) { |
| DemuxerConfigs configs; |
| configs.audio_codec = audio_codec; |
| configs.audio_channels = 2; |
| configs.is_audio_encrypted = false; |
| configs.duration = duration; |
| |
| switch (audio_codec) { |
| case kCodecVorbis: { |
| configs.audio_sampling_rate = 44100; |
| scoped_refptr<DecoderBuffer> buffer = |
| ReadTestDataFile("vorbis-extradata"); |
| configs.audio_extra_data = std::vector<uint8>( |
| buffer->data(), buffer->data() + buffer->data_size()); |
| } break; |
| |
| case kCodecAAC: { |
| configs.audio_sampling_rate = 44100; |
| configs.audio_extra_data = {0x12, 0x10}; |
| } break; |
| |
| default: |
| // Other codecs are not supported by this helper. |
| NOTREACHED(); |
| break; |
| } |
| |
| return configs; |
| } |
| |
| DemuxerConfigs TestDataFactory::CreateVideoConfigs( |
| VideoCodec video_codec, |
| base::TimeDelta duration, |
| const gfx::Size& video_size) { |
| DemuxerConfigs configs; |
| configs.video_codec = video_codec; |
| configs.video_size = video_size; |
| configs.is_video_encrypted = false; |
| configs.duration = duration; |
| |
| return configs; |
| } |
| |
| TestDataFactory::TestDataFactory(const char* file_name_template, |
| base::TimeDelta duration, |
| base::TimeDelta frame_period) |
| : duration_(duration), |
| frame_period_(frame_period), |
| total_chunks_(0), |
| starvation_mode_(false), |
| eos_reached_(false) { |
| LoadPackets(file_name_template); |
| } |
| |
| TestDataFactory::~TestDataFactory() {} |
| |
| bool TestDataFactory::CreateChunk(DemuxerData* chunk, base::TimeDelta* delay) { |
| DCHECK(chunk); |
| DCHECK(delay); |
| |
| if (eos_reached_) |
| return false; |
| |
| *delay = base::TimeDelta(); |
| |
| if (!total_chunks_ && |
| HasReconfigForInterval(base::TimeDelta::FromMilliseconds(-1), |
| base::TimeDelta())) { |
| // Since the configs AU has to come last in the chunk the initial configs |
| // preceeding any other data has to be the only unit in the chunk. |
| AddConfiguration(chunk); |
| ++total_chunks_; |
| return true; |
| } |
| |
| for (int i = 0; i < 4; ++i) { |
| chunk->access_units.push_back(AccessUnit()); |
| AccessUnit& unit = chunk->access_units.back(); |
| |
| unit.status = DemuxerStream::kOk; |
| unit.timestamp = regular_pts_; |
| unit.data = packet_[i]; |
| |
| regular_pts_ += frame_period_; |
| } |
| |
| if (chunk->access_units.back().timestamp > duration_) { |
| eos_reached_ = true; |
| |
| // Replace last access unit with stand-alone EOS if we exceeded duration. |
| if (!starvation_mode_) { |
| AccessUnit& unit = chunk->access_units.back(); |
| unit.is_end_of_stream = true; |
| unit.data.clear(); |
| } |
| } |
| |
| // Allow for modification by subclasses. |
| ModifyChunk(chunk); |
| |
| // Maintain last PTS. |
| for (const AccessUnit& unit : chunk->access_units) { |
| if (last_pts_ < unit.timestamp && !unit.data.empty()) |
| last_pts_ = unit.timestamp; |
| } |
| |
| // Replace last access unit with |kConfigChanged| if we have a config |
| // request for the chunk's interval. |
| base::TimeDelta new_chunk_begin_pts = regular_pts_; |
| |
| // The interval is [first, last) |
| if (HasReconfigForInterval(chunk_begin_pts_, new_chunk_begin_pts)) { |
| eos_reached_ = false; |
| regular_pts_ -= frame_period_; |
| chunk->access_units.pop_back(); |
| AddConfiguration(chunk); |
| } |
| chunk_begin_pts_ = new_chunk_begin_pts; |
| |
| ++total_chunks_; |
| return true; |
| } |
| |
| void TestDataFactory::SeekTo(const base::TimeDelta& seek_time) { |
| regular_pts_ = seek_time; |
| chunk_begin_pts_ = seek_time; |
| last_pts_ = base::TimeDelta(); |
| eos_reached_ = false; |
| } |
| |
| void TestDataFactory::RequestInitialConfigs() { |
| reconfigs_.insert(base::TimeDelta::FromMilliseconds(-1)); |
| } |
| |
| void TestDataFactory::RequestConfigChange(base::TimeDelta config_position) { |
| reconfigs_.insert(config_position); |
| } |
| |
| void TestDataFactory::LoadPackets(const char* file_name_template) { |
| for (int i = 0; i < 4; ++i) { |
| scoped_refptr<DecoderBuffer> buffer = |
| ReadTestDataFile(base::StringPrintf(file_name_template, i)); |
| packet_[i] = std::vector<uint8>(buffer->data(), |
| buffer->data() + buffer->data_size()); |
| } |
| } |
| |
| bool TestDataFactory::HasReconfigForInterval(base::TimeDelta left, |
| base::TimeDelta right) const { |
| // |first| points to an element greater or equal to |left|. |
| PTSSet::const_iterator first = reconfigs_.lower_bound(left); |
| |
| // |last| points to an element greater or equal to |right|. |
| PTSSet::const_iterator last = reconfigs_.lower_bound(right); |
| |
| return std::distance(first, last); |
| } |
| |
| void TestDataFactory::AddConfiguration(DemuxerData* chunk) { |
| DCHECK(chunk); |
| chunk->access_units.push_back(AccessUnit()); |
| AccessUnit& unit = chunk->access_units.back(); |
| unit.status = DemuxerStream::kConfigChanged; |
| |
| DCHECK(chunk->demuxer_configs.empty()); |
| chunk->demuxer_configs.push_back(GetConfigs()); |
| } |
| |
| } // namespace media |