| // 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 |