blob: 6199122dbabc771e93fb2b3923a8d44c5b53ede1 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/formats/common/stream_parser_test_base.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "media/base/media_log.h"
#include "media/base/media_track.h"
#include "media/base/media_tracks.h"
#include "media/base/stream_parser.h"
#include "media/base/test_data_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
static std::string BufferQueueToString(
const StreamParser::BufferQueue& buffers) {
std::stringstream ss;
ss << "{";
for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
itr != buffers.end();
++itr) {
ss << " " << (*itr)->timestamp().InMilliseconds();
if ((*itr)->is_key_frame())
ss << "K";
}
ss << " }";
return ss.str();
}
StreamParserTestBase::StreamParserTestBase(
std::unique_ptr<StreamParser> stream_parser)
: parser_(std::move(stream_parser)) {
parser_->Init(
base::BindOnce(&StreamParserTestBase::OnInitDone, base::Unretained(this)),
base::BindRepeating(&StreamParserTestBase::OnNewConfig,
base::Unretained(this)),
base::BindRepeating(&StreamParserTestBase::OnNewBuffers,
base::Unretained(this)),
base::BindRepeating(&StreamParserTestBase::OnKeyNeeded,
base::Unretained(this)),
base::BindRepeating(&StreamParserTestBase::OnNewSegment,
base::Unretained(this)),
base::BindRepeating(&StreamParserTestBase::OnEndOfSegment,
base::Unretained(this)),
&media_log_);
}
StreamParserTestBase::~StreamParserTestBase() = default;
std::string StreamParserTestBase::ParseFile(const std::string& filename,
int append_bytes) {
CHECK_GE(append_bytes, 0);
results_stream_.clear();
scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
size_t start = 0;
size_t end = buffer->size();
do {
size_t chunk_size = std::min(static_cast<size_t>(append_bytes),
static_cast<size_t>(end - start));
// Attempt to incrementally parse each appended chunk to test out the
// parser's internal management of input queue and pending data bytes.
EXPECT_TRUE(AppendAllDataThenParseInPieces(
(*buffer).subspan(start, chunk_size),
(chunk_size > 7) ? (chunk_size - 7) : chunk_size));
start += chunk_size;
} while (start < end);
return results_stream_.str();
}
std::string StreamParserTestBase::ParseData(base::span<const uint8_t> data) {
results_stream_.clear();
EXPECT_TRUE(AppendAllDataThenParseInPieces(data, data.size()));
return results_stream_.str();
}
bool StreamParserTestBase::AppendAllDataThenParseInPieces(
base::span<const uint8_t> data,
size_t piece_size) {
if (!parser_->AppendToParseBuffer(data)) {
return false;
}
// Also verify that the expected number of pieces is needed to fully parse
// `data`.
size_t expected_remaining_data = data.size();
bool has_more_data = true;
// A zero-length append still needs a single iteration of parse.
while (has_more_data) {
StreamParser::ParseStatus parse_result = parser_->Parse(piece_size);
if (parse_result == StreamParser::ParseStatus::kFailed) {
return false;
}
has_more_data =
parse_result == StreamParser::ParseStatus::kSuccessHasMoreData;
EXPECT_EQ(piece_size < expected_remaining_data, has_more_data);
if (has_more_data) {
expected_remaining_data -= piece_size;
} else {
EXPECT_EQ(parse_result, StreamParser::ParseStatus::kSuccess);
}
}
return true;
}
void StreamParserTestBase::OnInitDone(
const StreamParser::InitParameters& params) {
DVLOG(1) << __func__ << "(" << params.duration.InMilliseconds() << ")";
}
bool StreamParserTestBase::OnNewConfig(std::unique_ptr<MediaTracks> tracks) {
DVLOG(1) << __func__ << ": got " << tracks->tracks().size() << " tracks";
EXPECT_EQ(tracks->tracks().size(), 1u);
const auto& track = tracks->tracks()[0];
EXPECT_EQ(track->type(), MediaTrack::Type::kAudio);
audio_track_id_ = track->stream_id();
last_audio_config_ = tracks->getAudioConfig(track->stream_id());
EXPECT_TRUE(last_audio_config_.IsValidConfig());
// This common test utility only ever expects a single audio track in any
// tested init segment.
const auto& audio_configs = tracks->GetAudioConfigs();
EXPECT_EQ(audio_configs.size(), 1u);
const auto& itr = audio_configs.find(track->stream_id());
EXPECT_NE(itr, audio_configs.end());
EXPECT_TRUE(last_audio_config_.Matches(itr->second));
EXPECT_EQ(tracks->GetVideoConfigs().size(), 0u);
return true;
}
bool StreamParserTestBase::OnNewBuffers(
const StreamParser::BufferQueueMap& buffer_queue_map) {
EXPECT_EQ(1u, buffer_queue_map.size());
const auto& itr_audio = buffer_queue_map.find(audio_track_id_);
EXPECT_NE(buffer_queue_map.end(), itr_audio);
const StreamParser::BufferQueue& audio_buffers = itr_audio->second;
EXPECT_FALSE(audio_buffers.empty());
// Ensure that track ids are properly assigned on all emitted buffers.
for (const auto& buf : audio_buffers) {
EXPECT_EQ(audio_track_id_, buf->track_id());
}
const std::string buffers_str = BufferQueueToString(audio_buffers);
DVLOG(1) << __func__ << " : " << buffers_str;
results_stream_ << buffers_str;
return true;
}
void StreamParserTestBase::OnKeyNeeded(EmeInitDataType type,
const std::vector<uint8_t>& init_data) {
DVLOG(1) << __func__ << "(" << static_cast<int>(type) << ", "
<< init_data.size() << ")";
}
void StreamParserTestBase::OnNewSegment() {
DVLOG(1) << __func__;
results_stream_ << "NewSegment";
}
void StreamParserTestBase::OnEndOfSegment() {
DVLOG(1) << __func__;
results_stream_ << "EndOfSegment";
}
} // namespace media