blob: e9012d8572cc6da4407b2feb5152bb3e420a5a42 [file] [log] [blame]
// 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