#include "media/base/audio_buffer_queue.h"
#include <algorithm>
#include "base/check_op.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_timestamp_helper.h"
namespace media {
AudioBufferQueue::AudioBufferQueue() { Clear(); }
AudioBufferQueue::~AudioBufferQueue() = default;
void AudioBufferQueue::Clear() {
buffers_.clear();
front_buffer_offset_ = 0;
frames_ = 0;
}
void AudioBufferQueue::Append(scoped_refptr<AudioBuffer> buffer_in) {
frames_ += buffer_in->frame_count();
CHECK_GT(frames_, 0);
buffers_.push_back(std::move(buffer_in));
}
int AudioBufferQueue::ReadFrames(int frames,
int dest_frame_offset,
AudioBus* dest) {
DCHECK_GE(dest->frames(), frames + dest_frame_offset);
return InternalRead(frames, true, 0, dest_frame_offset, dest);
}
int AudioBufferQueue::PeekFrames(int frames,
int source_frame_offset,
int dest_frame_offset,
AudioBus* dest) {
DCHECK_GE(dest->frames(), frames);
return InternalRead(
frames, false, source_frame_offset, dest_frame_offset, dest);
}
void AudioBufferQueue::SeekFrames(int frames) {
CHECK_LE(frames, frames_);
int taken = InternalRead(frames, true, 0, 0, nullptr);
DCHECK_EQ(taken, frames);
}
std::optional<base::TimeDelta> AudioBufferQueue::FrontTimestamp() const {
if (buffers_.empty()) {
return std::nullopt;
}
return buffers_.front()->timestamp() +
AudioTimestampHelper::FramesToTime(front_buffer_offset_,
buffers_.front()->sample_rate());
}
int AudioBufferQueue::InternalRead(int frames,
bool advance_position,
int source_frame_offset,
int dest_frame_offset,
AudioBus* dest) {
if (buffers_.empty())
return 0;
if (buffers_.front()->IsBitstreamFormat()) {
DCHECK(!dest_frame_offset ||
dest_frame_offset == dest->GetBitstreamFrames());
DCHECK(!source_frame_offset);
const auto& buffer = buffers_.front();
int taken = buffer->frame_count();
if (dest) {
CHECK_GE(frames, buffer->frame_count());
buffer->ReadFrames(buffer->frame_count(), 0, dest_frame_offset, dest);
}
if (advance_position) {
frames_ -= taken;
DCHECK_GE(frames_, 0);
buffers_.pop_front();
}
return taken;
}
int taken = 0;
BufferQueue::iterator current_buffer = buffers_.begin();
int current_buffer_offset = front_buffer_offset_;
int frames_to_skip = source_frame_offset;
while (taken < frames) {
if (current_buffer == buffers_.end())
break;
scoped_refptr<AudioBuffer> buffer = *current_buffer;
int remaining_frames_in_buffer =
buffer->frame_count() - current_buffer_offset;
if (frames_to_skip > 0) {
int skipped = std::min(remaining_frames_in_buffer, frames_to_skip);
current_buffer_offset += skipped;
frames_to_skip -= skipped;
} else {
int copied = std::min(frames - taken, remaining_frames_in_buffer);
if (dest) {
buffer->ReadFrames(
copied, current_buffer_offset, dest_frame_offset + taken, dest);
}
taken += copied;
current_buffer_offset += copied;
}
if (current_buffer_offset == buffer->frame_count()) {
BufferQueue::iterator next = current_buffer + 1;
if (next == buffers_.end())
break;
current_buffer = next;
current_buffer_offset = 0;
}
}
if (advance_position) {
frames_ -= taken;
DCHECK_GE(frames_, 0);
buffers_.erase(buffers_.begin(), current_buffer);
front_buffer_offset_ = current_buffer_offset;
}
return taken;
}
}