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