blob: d1d84ab98e663f07d514a1e98371299a1ab77fb6 [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/media/base/decoder_buffer_cache.h"
#include <algorithm>
#include <cstring>
#include <utility>
namespace cobalt {
namespace media {
namespace {
bool SafeStrEqual(const char* ptr1, const char* ptr2) {
if (!ptr1 || !ptr2) {
return ptr1 == ptr2;
}
return strcmp(ptr1, ptr2) == 0;
}
} // namespace
bool operator!=(const SbMediaAudioStreamInfo& left,
const SbMediaAudioStreamInfo& right) {
if (left.codec == kSbMediaAudioCodecNone) {
return right.codec != kSbMediaAudioCodecNone;
}
return left.codec != right.codec || !SafeStrEqual(left.mime, right.mime) ||
left.number_of_channels != right.number_of_channels ||
left.samples_per_second != right.samples_per_second ||
left.bits_per_sample != right.bits_per_sample ||
left.audio_specific_config_size != right.audio_specific_config_size ||
memcmp(left.audio_specific_config, right.audio_specific_config,
left.audio_specific_config_size) != 0;
}
bool operator!=(const SbMediaVideoStreamInfo& left,
const SbMediaVideoStreamInfo& right) {
if (left.codec == kSbMediaVideoCodecNone) {
return right.codec != kSbMediaVideoCodecNone;
}
return left.codec != right.codec || !SafeStrEqual(left.mime, right.mime) ||
!SafeStrEqual(left.max_video_capabilities,
right.max_video_capabilities) ||
left.frame_width != right.frame_width ||
left.frame_height != right.frame_height ||
memcmp(&left.color_metadata, &right.color_metadata,
sizeof(SbMediaColorMetadata)) != 0;
}
DecoderBufferCache::BufferGroup::Segment::Segment(
const SbMediaAudioStreamInfo& stream_info) {
DCHECK_NE(stream_info.codec, kSbMediaAudioCodecNone);
audio_stream_info = stream_info;
if (stream_info.audio_specific_config_size > 0) {
auto specific_config =
static_cast<const uint8_t*>(stream_info.audio_specific_config);
audio_specific_config.assign(
specific_config,
specific_config + stream_info.audio_specific_config_size);
audio_stream_info.audio_specific_config = audio_specific_config.data();
}
if (stream_info.mime) {
mime = stream_info.mime;
audio_stream_info.mime = mime.c_str();
}
}
DecoderBufferCache::BufferGroup::Segment::Segment(
const SbMediaVideoStreamInfo& stream_info) {
DCHECK_NE(stream_info.codec, kSbMediaVideoCodecNone);
video_stream_info = stream_info;
if (stream_info.mime) {
mime = stream_info.mime;
video_stream_info.mime = mime.c_str();
}
if (stream_info.max_video_capabilities) {
max_video_capabilities = stream_info.max_video_capabilities;
video_stream_info.max_video_capabilities = max_video_capabilities.c_str();
}
}
DecoderBufferCache::BufferGroup::BufferGroup(DemuxerStream::Type type)
: type_(type) {}
void DecoderBufferCache::BufferGroup::AddBuffers(
const DecoderBuffers& buffers, const SbMediaAudioStreamInfo& stream_info) {
DCHECK(type_ == DemuxerStream::Type::AUDIO);
if (buffers.empty()) {
return;
}
if (segments_.empty() || segments_.back().audio_stream_info != stream_info) {
AddStreamInfo(stream_info);
}
AddBuffers(buffers);
}
void DecoderBufferCache::BufferGroup::AddBuffers(
const DecoderBuffers& buffers, const SbMediaVideoStreamInfo& stream_info) {
DCHECK(type_ == DemuxerStream::Type::VIDEO);
if (buffers.empty()) {
return;
}
if (segments_.empty() || segments_.back().video_stream_info != stream_info) {
AddStreamInfo(stream_info);
}
AddBuffers(buffers);
}
void DecoderBufferCache::BufferGroup::ClearSegmentsBeforeMediaTime(
const base::TimeDelta& media_time) {
// Use K to denote a key frame and N for non-key frame. If the cache contains
// K N N N N N N N N K N N N N N N N N K N N N N N N N N
// |
// media_time
// Then we should remove everything before the key frame before the
// |media_time| and turn the cache into:
// K N N N N N N N N K N N N N N N N N
// |
// media_time
// So we need at least two keyframes before we can remove any buffers.
while (key_frame_timestamps_.size() > 1 &&
key_frame_timestamps_[1] <= media_time) {
key_frame_timestamps_.pop_front();
}
if (key_frame_timestamps_.empty()) {
return;
}
base::TimeDelta key_frame_timestamp = key_frame_timestamps_.front();
// Remove all buffers and groups before the key frame timestamp.
while (!segments_.empty()) {
auto& first_segment_buffers = segments_.front().buffers;
while (!first_segment_buffers.empty()) {
auto& buffer = first_segment_buffers.front();
if (buffer->is_key_frame() &&
buffer->timestamp() == key_frame_timestamp) {
return;
}
first_segment_buffers.pop_front();
if (segment_index_ == 0) {
if (buffer_index_ == 0) {
LOG(ERROR) << "Trying to remove unwritten buffers.";
} else {
buffer_index_--;
}
}
}
if (first_segment_buffers.empty()) {
DCHECK_GT(segments_.size(), 1);
DCHECK_GT(segment_index_, 0);
segments_.pop_front();
segment_index_--;
}
}
DCHECK(!segments_.empty() && segment_index_ < segments_.size());
DCHECK(buffer_index_ < segments_[segment_index_].buffers.size());
}
void DecoderBufferCache::BufferGroup::ClearAll() {
segments_.clear();
key_frame_timestamps_.clear();
segment_index_ = 0;
buffer_index_ = 0;
}
bool DecoderBufferCache::BufferGroup::HasMoreBuffers() const {
if (segments_.size() == 0) {
return false;
}
DCHECK(segment_index_ < segments_.size());
DCHECK(buffer_index_ <= segments_[segment_index_].buffers.size());
if (buffer_index_ == segments_[segment_index_].buffers.size()) {
DCHECK(segment_index_ == segments_.size() - 1);
return false;
}
return true;
}
template <typename StreamInfo>
void DecoderBufferCache::BufferGroup::AddStreamInfo(
const StreamInfo& stream_info) {
segments_.emplace_back(stream_info);
AdvanceGroupIndexIfNecessary();
}
void DecoderBufferCache::BufferGroup::AddBuffers(
const DecoderBuffers& buffers) {
DCHECK(!segments_.empty() && segment_index_ < segments_.size());
DCHECK(!buffers.empty());
auto& last_segment_buffers = segments_.back().buffers;
for (auto buffer : buffers) {
if (!buffer->end_of_stream() && buffer->is_key_frame()) {
key_frame_timestamps_.push_back(buffer->timestamp());
}
}
last_segment_buffers.insert(last_segment_buffers.end(), buffers.begin(),
buffers.end());
}
void DecoderBufferCache::BufferGroup::ReadBuffers(
DecoderBuffers* buffers, size_t max_buffers_per_read,
SbMediaAudioStreamInfo* stream_info) {
DCHECK_EQ(type_, DemuxerStream::AUDIO);
DCHECK_GT(max_buffers_per_read, 0);
DCHECK(HasMoreBuffers());
*stream_info = segments_[segment_index_].audio_stream_info;
ReadBuffers(buffers, max_buffers_per_read);
}
void DecoderBufferCache::BufferGroup::ReadBuffers(
DecoderBuffers* buffers, size_t max_buffers_per_read,
SbMediaVideoStreamInfo* stream_info) {
DCHECK_EQ(type_, DemuxerStream::VIDEO);
DCHECK_GT(max_buffers_per_read, 0);
DCHECK(HasMoreBuffers());
*stream_info = segments_[segment_index_].video_stream_info;
ReadBuffers(buffers, max_buffers_per_read);
}
void DecoderBufferCache::BufferGroup::ResetIndex() {
segment_index_ = 0;
buffer_index_ = 0;
}
void DecoderBufferCache::BufferGroup::AdvanceGroupIndexIfNecessary() {
DCHECK(segment_index_ < segments_.size());
DCHECK(buffer_index_ <= segments_[segment_index_].buffers.size());
if (buffer_index_ == segments_[segment_index_].buffers.size() &&
segment_index_ + 1 < segments_.size()) {
segment_index_++;
buffer_index_ = 0;
}
}
void DecoderBufferCache::BufferGroup::ReadBuffers(DecoderBuffers* buffers,
size_t max_buffers_per_read) {
DCHECK(segment_index_ < segments_.size());
DCHECK(buffer_index_ < segments_[segment_index_].buffers.size());
auto& current_segment_buffers = segments_[segment_index_].buffers;
size_t buffers_to_read = std::min(
max_buffers_per_read, current_segment_buffers.size() - buffer_index_);
DCHECK_GT(buffers_to_read, 0);
buffers->insert(
buffers->end(), current_segment_buffers.begin() + buffer_index_,
current_segment_buffers.begin() + buffer_index_ + buffers_to_read);
buffer_index_ += buffers_to_read;
AdvanceGroupIndexIfNecessary();
}
DecoderBufferCache::DecoderBufferCache()
: audio_buffer_group_(DemuxerStream::Type::AUDIO),
video_buffer_group_(DemuxerStream::Type::VIDEO) {}
void DecoderBufferCache::AddBuffers(const DecoderBuffers& buffers,
const SbMediaAudioStreamInfo& stream_info) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
audio_buffer_group_.AddBuffers(buffers, stream_info);
}
void DecoderBufferCache::AddBuffers(const DecoderBuffers& buffers,
const SbMediaVideoStreamInfo& stream_info) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
video_buffer_group_.AddBuffers(buffers, stream_info);
}
void DecoderBufferCache::ClearSegmentsBeforeMediaTime(
const base::TimeDelta& media_time) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
audio_buffer_group_.ClearSegmentsBeforeMediaTime(media_time);
video_buffer_group_.ClearSegmentsBeforeMediaTime(media_time);
}
void DecoderBufferCache::ClearAll() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
audio_buffer_group_.ClearAll();
video_buffer_group_.ClearAll();
}
void DecoderBufferCache::StartResuming() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
audio_buffer_group_.ResetIndex();
video_buffer_group_.ResetIndex();
}
bool DecoderBufferCache::HasMoreBuffers(DemuxerStream::Type type) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (type == DemuxerStream::Type::AUDIO) {
return audio_buffer_group_.HasMoreBuffers();
}
DCHECK(type == DemuxerStream::Type::VIDEO);
return video_buffer_group_.HasMoreBuffers();
}
void DecoderBufferCache::ReadBuffers(DecoderBuffers* buffers,
size_t max_buffers_per_read,
SbMediaAudioStreamInfo* stream_info) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(buffers);
DCHECK(buffers->empty());
DCHECK(stream_info);
if (max_buffers_per_read == 0 || !audio_buffer_group_.HasMoreBuffers()) {
return;
}
audio_buffer_group_.ReadBuffers(buffers, max_buffers_per_read, stream_info);
DCHECK(!buffers->empty());
}
void DecoderBufferCache::ReadBuffers(DecoderBuffers* buffers,
size_t max_buffers_per_read,
SbMediaVideoStreamInfo* stream_info) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(buffers);
DCHECK(buffers->empty());
CHECK(stream_info);
if (max_buffers_per_read == 0 || !video_buffer_group_.HasMoreBuffers()) {
return;
}
video_buffer_group_.ReadBuffers(buffers, max_buffers_per_read, stream_info);
DCHECK(!buffers->empty());
}
} // namespace media
} // namespace cobalt