| // Copyright 2013 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. |
| |
| // Modifications Copyright 2017 Google Inc. 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 "starboard/shared/starboard/player/filter/decoded_audio_queue.h" |
| |
| #include <algorithm> |
| |
| #include "starboard/log.h" |
| #include "starboard/media.h" |
| |
| namespace starboard { |
| namespace shared { |
| namespace starboard { |
| namespace player { |
| namespace filter { |
| |
| DecodedAudioQueue::DecodedAudioQueue() { |
| Clear(); |
| } |
| DecodedAudioQueue::~DecodedAudioQueue() {} |
| |
| void DecodedAudioQueue::Clear() { |
| buffers_.clear(); |
| current_buffer_ = buffers_.begin(); |
| current_buffer_offset_ = 0; |
| frames_ = 0; |
| } |
| |
| void DecodedAudioQueue::Append( |
| const scoped_refptr<DecodedAudio>& decoded_audio) { |
| SB_DCHECK(decoded_audio->sample_type() == kSbMediaAudioSampleTypeFloat32); |
| SB_DCHECK(decoded_audio->storage_type() == |
| kSbMediaAudioFrameStorageTypeInterleaved) |
| << decoded_audio->storage_type(); |
| // Add the buffer to the queue. Inserting into deque invalidates all |
| // iterators, so point to the first buffer. |
| buffers_.push_back(decoded_audio); |
| current_buffer_ = buffers_.begin(); |
| |
| // Update the |frames_| counter since we have added frames. |
| frames_ += decoded_audio->frames(); |
| SB_CHECK(frames_ > 0); // make sure it doesn't overflow. |
| } |
| |
| int DecodedAudioQueue::ReadFrames(int frames, |
| int dest_frame_offset, |
| DecodedAudio* dest) { |
| SB_DCHECK(dest->frames() >= frames + dest_frame_offset); |
| return InternalRead(frames, true, 0, dest_frame_offset, dest); |
| } |
| |
| int DecodedAudioQueue::PeekFrames(int frames, |
| int source_frame_offset, |
| int dest_frame_offset, |
| DecodedAudio* dest) { |
| SB_DCHECK(dest->frames() >= frames); |
| return InternalRead(frames, false, source_frame_offset, dest_frame_offset, |
| dest); |
| } |
| |
| void DecodedAudioQueue::SeekFrames(int frames) { |
| // Perform seek only if we have enough bytes in the queue. |
| SB_CHECK(frames <= frames_); |
| int taken = InternalRead(frames, true, 0, 0, NULL); |
| SB_DCHECK(taken == frames); |
| } |
| |
| int DecodedAudioQueue::InternalRead(int frames, |
| bool advance_position, |
| int source_frame_offset, |
| int dest_frame_offset, |
| DecodedAudio* dest) { |
| // Counts how many frames are actually read from the buffer queue. |
| int taken = 0; |
| BufferQueue::iterator current_buffer = current_buffer_; |
| int current_buffer_offset = current_buffer_offset_; |
| |
| int frames_to_skip = source_frame_offset; |
| while (taken < frames) { |
| // |current_buffer| is valid since the first time this buffer is appended |
| // with data. Make sure there is data to be processed. |
| if (current_buffer == buffers_.end()) |
| break; |
| |
| scoped_refptr<DecodedAudio> buffer = *current_buffer; |
| |
| int remaining_frames_in_buffer = buffer->frames() - current_buffer_offset; |
| |
| if (frames_to_skip > 0) { |
| // If there are frames to skip, do it first. May need to skip into |
| // subsequent buffers. |
| int skipped = std::min(remaining_frames_in_buffer, frames_to_skip); |
| current_buffer_offset += skipped; |
| frames_to_skip -= skipped; |
| } else { |
| // Find the right amount to copy from the current buffer. We shall copy no |
| // more than |frames| frames in total and each single step copies no more |
| // than the current buffer size. |
| int copied = std::min(frames - taken, remaining_frames_in_buffer); |
| |
| // if |dest| is NULL, there's no need to copy. |
| if (dest) { |
| SB_DCHECK(buffer->channels() == dest->channels()); |
| const float* source = reinterpret_cast<const float*>(buffer->buffer()) + |
| buffer->channels() * current_buffer_offset; |
| float* destination = reinterpret_cast<float*>(dest->buffer()) + |
| dest->channels() * (dest_frame_offset + taken); |
| SbMemoryCopy(destination, source, copied * dest->channels() * 4); |
| } |
| |
| // Increase total number of frames copied, which regulates when to end |
| // this loop. |
| taken += copied; |
| |
| // We have read |copied| frames from the current buffer. Advance the |
| // offset. |
| current_buffer_offset += copied; |
| } |
| |
| // Has the buffer has been consumed? |
| if (current_buffer_offset == buffer->frames()) { |
| // If we are at the last buffer, no more data to be copied, so stop. |
| BufferQueue::iterator next = current_buffer + 1; |
| if (next == buffers_.end()) |
| break; |
| |
| // Advances the iterator. |
| current_buffer = next; |
| current_buffer_offset = 0; |
| } |
| } |
| |
| if (advance_position) { |
| // Update the appropriate values since |taken| frames have been copied out. |
| frames_ -= taken; |
| SB_DCHECK(frames_ >= 0); |
| SB_DCHECK(current_buffer_ != buffers_.end() || frames_ == 0); |
| |
| // Remove any buffers before the current buffer as there is no going |
| // backwards. |
| buffers_.erase(buffers_.begin(), current_buffer); |
| current_buffer_ = buffers_.begin(); |
| current_buffer_offset_ = current_buffer_offset; |
| } |
| |
| return taken; |
| } |
| |
| } // namespace filter |
| } // namespace player |
| } // namespace starboard |
| } // namespace shared |
| } // namespace starboard |