blob: c0593fe4c02873e428a153caf2aaa9859f4eeed2 [file] [log] [blame]
// Copyright 2014 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.
#include "media/filters/frame_processor_base.h"
#include <cstdlib>
#include "base/stl_util.h"
#include "media/base/buffers.h"
namespace media {
MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream)
: last_decode_timestamp_(kNoTimestamp()),
last_frame_duration_(kNoTimestamp()),
highest_presentation_timestamp_(kNoTimestamp()),
needs_random_access_point_(true),
stream_(stream) {
DCHECK(stream_);
}
MseTrackBuffer::~MseTrackBuffer() {
DVLOG(2) << __FUNCTION__ << "()";
}
void MseTrackBuffer::Reset() {
DVLOG(2) << __FUNCTION__ << "()";
last_decode_timestamp_ = kNoTimestamp();
last_frame_duration_ = kNoTimestamp();
highest_presentation_timestamp_ = kNoTimestamp();
needs_random_access_point_ = true;
}
void MseTrackBuffer::SetHighestPresentationTimestampIfIncreased(
base::TimeDelta timestamp) {
if (highest_presentation_timestamp_ == kNoTimestamp() ||
timestamp > highest_presentation_timestamp_) {
highest_presentation_timestamp_ = timestamp;
}
}
FrameProcessorBase::FrameProcessorBase()
: sequence_mode_(false),
group_start_timestamp_(kNoTimestamp()) {}
FrameProcessorBase::~FrameProcessorBase() {
DVLOG(2) << __FUNCTION__ << "()";
STLDeleteValues(&track_buffers_);
}
void FrameProcessorBase::SetGroupStartTimestampIfInSequenceMode(
base::TimeDelta timestamp_offset) {
DVLOG(2) << __FUNCTION__ << "(" << timestamp_offset.InSecondsF() << ")";
DCHECK(kNoTimestamp() != timestamp_offset);
if (sequence_mode_)
group_start_timestamp_ = timestamp_offset;
// Changes to timestampOffset should invalidate the preroll buffer.
audio_preroll_buffer_ = NULL;
}
bool FrameProcessorBase::AddTrack(StreamParser::TrackId id,
ChunkDemuxerStream* stream) {
DVLOG(2) << __FUNCTION__ << "(): id=" << id;
MseTrackBuffer* existing_track = FindTrack(id);
DCHECK(!existing_track);
if (existing_track)
return false;
track_buffers_[id] = new MseTrackBuffer(stream);
return true;
}
bool FrameProcessorBase::UpdateTrack(StreamParser::TrackId old_id,
StreamParser::TrackId new_id) {
DVLOG(2) << __FUNCTION__ << "() : old_id=" << old_id << ", new_id=" << new_id;
if (old_id == new_id || !FindTrack(old_id) || FindTrack(new_id))
return false;
track_buffers_[new_id] = track_buffers_[old_id];
CHECK_EQ(1u, track_buffers_.erase(old_id));
return true;
}
void FrameProcessorBase::SetAllTrackBuffersNeedRandomAccessPoint() {
for (TrackBufferMap::iterator itr = track_buffers_.begin();
itr != track_buffers_.end();
++itr) {
itr->second->set_needs_random_access_point(true);
}
}
void FrameProcessorBase::Reset() {
DVLOG(2) << __FUNCTION__ << "()";
for (TrackBufferMap::iterator itr = track_buffers_.begin();
itr != track_buffers_.end(); ++itr) {
itr->second->Reset();
}
}
MseTrackBuffer* FrameProcessorBase::FindTrack(StreamParser::TrackId id) {
TrackBufferMap::iterator itr = track_buffers_.find(id);
if (itr == track_buffers_.end())
return NULL;
return itr->second;
}
void FrameProcessorBase::NotifyNewMediaSegmentStarting(
base::TimeDelta segment_timestamp) {
DVLOG(2) << __FUNCTION__ << "(" << segment_timestamp.InSecondsF() << ")";
for (TrackBufferMap::iterator itr = track_buffers_.begin();
itr != track_buffers_.end();
++itr) {
itr->second->stream()->OnNewMediaSegment(segment_timestamp);
}
}
bool FrameProcessorBase::HandlePartialAppendWindowTrimming(
base::TimeDelta append_window_start,
base::TimeDelta append_window_end,
const scoped_refptr<StreamParserBuffer>& buffer) {
DCHECK(buffer->duration() > base::TimeDelta());
DCHECK_EQ(DemuxerStream::AUDIO, buffer->type());
const base::TimeDelta frame_end_timestamp =
buffer->timestamp() + buffer->duration();
// Ignore any buffers which start after |append_window_start| or end after
// |append_window_end|. For simplicity, even those that start before
// |append_window_start|.
if (buffer->timestamp() > append_window_start ||
frame_end_timestamp > append_window_end) {
// TODO(dalecurtis): Partial append window trimming could also be done
// around |append_window_end|, but is not necessary since splice frames
// cover overlaps there.
return false;
}
// If the buffer is entirely before |append_window_start|, save it as preroll
// for the first buffer which overlaps |append_window_start|.
if (buffer->timestamp() < append_window_start &&
frame_end_timestamp <= append_window_start) {
audio_preroll_buffer_ = buffer;
return false;
}
// There's nothing to be done if we have no preroll and the buffer starts on
// the append window start.
if (buffer->timestamp() == append_window_start && !audio_preroll_buffer_)
return false;
// See if a partial discard can be done around |append_window_start|.
DCHECK(buffer->timestamp() <= append_window_start);
DCHECK(buffer->IsKeyframe());
DVLOG(1) << "Truncating buffer which overlaps append window start."
<< " presentation_timestamp " << buffer->timestamp().InSecondsF()
<< " append_window_start " << append_window_start.InSecondsF();
// If this isn't the first buffer discarded by the append window, try to use
// the last buffer discarded for preroll. This ensures that the partially
// trimmed buffer can be correctly decoded.
if (audio_preroll_buffer_) {
// We only want to use the preroll buffer if it directly precedes (less than
// one sample apart) the current buffer.
const int64 delta = std::abs((audio_preroll_buffer_->timestamp() +
audio_preroll_buffer_->duration() -
buffer->timestamp()).InMicroseconds());
if (delta < sample_duration_.InMicroseconds()) {
buffer->SetPrerollBuffer(audio_preroll_buffer_);
} else {
// TODO(dalecurtis): Add a MEDIA_LOG() for when this is dropped unused.
}
audio_preroll_buffer_ = NULL;
}
// Decrease the duration appropriately. We only need to shorten the buffer if
// it overlaps |append_window_start|.
if (buffer->timestamp() < append_window_start) {
buffer->set_discard_padding(std::make_pair(
append_window_start - buffer->timestamp(), base::TimeDelta()));
buffer->set_duration(frame_end_timestamp - append_window_start);
}
// Adjust the timestamp of this buffer forward to |append_window_start|. The
// timestamps are always set, even if |buffer|'s timestamp is already set to
// |append_window_start|, to ensure the preroll buffer is setup correctly.
buffer->set_timestamp(append_window_start);
buffer->SetDecodeTimestamp(append_window_start);
return true;
}
void FrameProcessorBase::OnPossibleAudioConfigUpdate(
const AudioDecoderConfig& config) {
DCHECK(config.IsValidConfig());
// Always clear the preroll buffer when a config update is received.
audio_preroll_buffer_ = NULL;
if (config.Matches(current_audio_config_))
return;
current_audio_config_ = config;
sample_duration_ = base::TimeDelta::FromSecondsD(
1.0 / current_audio_config_.samples_per_second());
}
} // namespace media