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 |