Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(363)

Unified Diff: media/filters/legacy_frame_processor.cc

Issue 191513002: Extract coded frame processing from SourceState into LegacyFrameProcessor (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase and address PS2 comments and nits Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/filters/legacy_frame_processor.cc
diff --git a/media/filters/legacy_frame_processor.cc b/media/filters/legacy_frame_processor.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1cac49a1a73228f5e573bca47f11e3ab767a6511
--- /dev/null
+++ b/media/filters/legacy_frame_processor.cc
@@ -0,0 +1,238 @@
+// 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/legacy_frame_processor.h"
+
+#include "media/base/buffers.h"
+#include "media/base/stream_parser_buffer.h"
+
+namespace media {
+
+LegacyFrameProcessor::LegacyFrameProcessor(
+ const IncreaseDurationCB& increase_duration_cb)
+ : FrameProcessorBase(increase_duration_cb) {
+ DVLOG(2) << __FUNCTION__ << "()";
+}
+
+LegacyFrameProcessor::~LegacyFrameProcessor() {
+ DVLOG(2) << __FUNCTION__ << "()";
+}
+
+void LegacyFrameProcessor::SetSequenceMode(bool sequence_mode) {
+ DVLOG(2) << __FUNCTION__ << "(" << sequence_mode << ")";
+
+ sequence_mode_ = sequence_mode;
+}
+
+bool LegacyFrameProcessor::ProcessFrames(
+ const StreamParser::BufferQueue& audio_buffers,
+ const StreamParser::BufferQueue& video_buffers,
+ const StreamParser::TextBufferQueueMap& text_map,
+ const base::TimeDelta& append_window_start,
+ const base::TimeDelta& append_window_end,
+ bool* new_media_segment,
+ base::TimeDelta* timestamp_offset) {
+ DVLOG(2) << __FUNCTION__ << "()";
+ DCHECK(new_media_segment);
+ DCHECK(timestamp_offset);
+
+ // NOTE: Legacy coded frame processing does not update timestamp offset.
+ base::TimeDelta offset = *timestamp_offset;
+
+ DCHECK(!audio_buffers.empty() || !video_buffers.empty() ||
+ !text_map.empty());
+
+ MseTrackBuffer* audio_track = FindTrack(kAudioTrackId);
+ DCHECK(audio_buffers.empty() || audio_track);
+
+ MseTrackBuffer* video_track = FindTrack(kVideoTrackId);
+ DCHECK(video_buffers.empty() || video_track);
+
+ // TODO(wolenetz): DCHECK + return false if any of these buffers have UNKNOWN
+ // type() in upcoming coded frame processing compliant implementation. See
+ // http://crbug.com/249422.
+
+ StreamParser::BufferQueue filtered_audio;
+ StreamParser::BufferQueue filtered_video;
+
+ if (audio_track) {
+ AdjustBufferTimestamps(audio_buffers, offset);
+ FilterWithAppendWindow(append_window_start, append_window_end,
+ audio_buffers, audio_track,
+ new_media_segment, &filtered_audio);
+ }
+
+ if (video_track) {
+ AdjustBufferTimestamps(video_buffers, offset);
+ FilterWithAppendWindow(append_window_start, append_window_end,
+ video_buffers, video_track,
+ new_media_segment, &filtered_video);
+ }
+
+ if ((!filtered_audio.empty() || !filtered_video.empty()) &&
+ *new_media_segment) {
+ // Find the earliest timestamp in the filtered buffers and use that for the
+ // segment start timestamp.
+ base::TimeDelta segment_timestamp = kNoTimestamp();
+
+ if (!filtered_audio.empty())
+ segment_timestamp = filtered_audio.front()->GetDecodeTimestamp();
+
+ if (!filtered_video.empty() &&
+ (segment_timestamp == kNoTimestamp() ||
+ filtered_video.front()->GetDecodeTimestamp() < segment_timestamp)) {
+ segment_timestamp = filtered_video.front()->GetDecodeTimestamp();
+ }
+
+ *new_media_segment = false;
acolwell GONE FROM CHROMIUM 2014/03/11 20:00:37 hmm. It is unfortunate that we need to pass this s
wolenetz 2014/03/12 00:46:14 I added a TODO @SourceState::new_media_segment_ in
+
+ for (TrackBufferMap::iterator itr = track_buffers_.begin();
+ itr != track_buffers_.end(); ++itr) {
+ itr->second->stream()->OnNewMediaSegment(segment_timestamp);
+ }
+ }
+
+ if (!filtered_audio.empty() &&
+ !AppendAndUpdateDuration(audio_track->stream(), filtered_audio)) {
+ return false;
+ }
+
+ if (!filtered_video.empty() &&
+ !AppendAndUpdateDuration(video_track->stream(), filtered_video)) {
+ return false;
+ }
+
+ if (text_map.empty())
+ return true;
+
+ // Process any buffers for each of the text tracks in the map.
+ bool all_text_buffers_empty = true;
+ for (StreamParser::TextBufferQueueMap::const_iterator itr = text_map.begin();
+ itr != text_map.end();
+ ++itr) {
+ const StreamParser::BufferQueue text_buffers = itr->second;
+ if (!text_buffers.empty()) {
+ all_text_buffers_empty = false;
+ if (!OnTextBuffers(itr->first, append_window_start, append_window_end,
+ offset, text_buffers, new_media_segment))
+ return false;
+ }
+ }
+
+ DCHECK(!all_text_buffers_empty);
+ return true;
+}
+
+void LegacyFrameProcessor::AdjustBufferTimestamps(
+ const StreamParser::BufferQueue& buffers,
+ const base::TimeDelta& timestamp_offset) {
+ if (timestamp_offset == base::TimeDelta())
+ return;
+
+ for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
+ itr != buffers.end(); ++itr) {
+ (*itr)->SetDecodeTimestamp(
+ (*itr)->GetDecodeTimestamp() + timestamp_offset);
+ (*itr)->set_timestamp((*itr)->timestamp() + timestamp_offset);
+ }
+}
+
+void LegacyFrameProcessor::FilterWithAppendWindow(
+ const base::TimeDelta& append_window_start,
+ const base::TimeDelta& append_window_end,
+ const StreamParser::BufferQueue& buffers,
+ MseTrackBuffer* track,
+ bool* new_media_segment,
+ StreamParser::BufferQueue* filtered_buffers) {
+ DCHECK(track);
+ DCHECK(new_media_segment);
+ DCHECK(filtered_buffers);
+
+ // This loop implements steps 1.9, 1.10, & 1.11 of the "Coded frame
+ // processing loop" in the Media Source Extensions spec.
+ // These steps filter out buffers that are not within the "append
+ // window" and handles resyncing on the next random access point
+ // (i.e., next keyframe) if a buffer gets dropped.
+ for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
+ itr != buffers.end(); ++itr) {
+ // Filter out buffers that are outside the append window. Anytime
+ // a buffer gets dropped we need to set |*needs_keyframe| to true
+ // because we can only resume decoding at keyframes.
+ base::TimeDelta presentation_timestamp = (*itr)->timestamp();
+
+ // TODO(acolwell): Change |frame_end_timestamp| value to
+ // |presentation_timestamp + (*itr)->duration()|, like the spec
+ // requires, once frame durations are actually present in all buffers.
+ base::TimeDelta frame_end_timestamp = presentation_timestamp;
+ if (presentation_timestamp < append_window_start ||
+ frame_end_timestamp > append_window_end) {
+ DVLOG(1) << "Dropping buffer outside append window."
+ << " presentation_timestamp "
+ << presentation_timestamp.InSecondsF();
+ track->set_needs_random_access_point(true);
+
+ // This triggers a discontinuity so we need to treat the next frames
+ // appended within the append window as if they were the beginning of a
+ // new segment.
+ *new_media_segment = true;
+ continue;
+ }
+
+ // If the track needs a keyframe, then filter out buffers until we
+ // encounter the next keyframe.
+ if (track->needs_random_access_point()) {
+ if (!(*itr)->IsKeyframe()) {
+ DVLOG(1) << "Dropping non-keyframe. presentation_timestamp "
+ << presentation_timestamp.InSecondsF();
+ continue;
+ }
+
+ track->set_needs_random_access_point(false);
+ }
+
+ filtered_buffers->push_back(*itr);
+ }
+}
+
+bool LegacyFrameProcessor::AppendAndUpdateDuration(
+ ChunkDemuxerStream* stream,
+ const StreamParser::BufferQueue& buffers) {
+ DCHECK(!buffers.empty());
+
+ if (!stream || !stream->Append(buffers))
+ return false;
+
+ increase_duration_cb_.Run(buffers.back()->timestamp(), stream);
+ return true;
+}
+
+bool LegacyFrameProcessor::OnTextBuffers(
+ StreamParser::TrackId text_track_id,
+ const base::TimeDelta& append_window_start,
+ const base::TimeDelta& append_window_end,
+ const base::TimeDelta& timestamp_offset,
+ const StreamParser::BufferQueue& buffers,
+ bool* new_media_segment) {
+ DCHECK(!buffers.empty());
+ DCHECK(text_track_id != kAudioTrackId && text_track_id != kVideoTrackId);
+ DCHECK(new_media_segment);
+
+ MseTrackBuffer* track = FindTrack(text_track_id);
+ if (!track)
+ return false;
+
+ AdjustBufferTimestamps(buffers, timestamp_offset);
+
+ StreamParser::BufferQueue filtered_buffers;
+ track->set_needs_random_access_point(false);
+ FilterWithAppendWindow(append_window_start, append_window_end,
+ buffers, track, new_media_segment, &filtered_buffers);
+
+ if (filtered_buffers.empty())
+ return true;
+
+ return AppendAndUpdateDuration(track->stream(), filtered_buffers);
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698