Index: media/base/stream_parser.cc |
diff --git a/media/base/stream_parser.cc b/media/base/stream_parser.cc |
index 12409194fede3cf0fcaa3d1671b7301416457409..f087a4b4d83e0ecf32191f4c2a31e7e7ab78cb66 100644 |
--- a/media/base/stream_parser.cc |
+++ b/media/base/stream_parser.cc |
@@ -4,10 +4,122 @@ |
#include "media/base/stream_parser.h" |
+#include "media/base/buffers.h" |
+#include "media/base/stream_parser_buffer.h" |
+ |
namespace media { |
StreamParser::StreamParser() {} |
StreamParser::~StreamParser() {} |
+static bool MergeBufferQueuesInternal( |
+ const std::vector<const StreamParser::BufferQueue*>& buffer_queues, |
+ StreamParser::BufferQueue* merged_buffers) { |
+ // Instead of std::merge usage, this method implements a custom merge because: |
+ // 1) |buffer_queues| may contain N queues, |
+ // 2) we must detect and return false if any of the queues in |buffer_queues| |
+ // is unsorted, and |
+ // 3) we must detect and return false if any of the buffers in |buffer_queues| |
+ // has a decode timestamp prior to the last, if any, buffer in |
+ // |merged_buffers|. |
+ // TODO(wolenetz/acolwell): Refactor stream parsers to eliminate need for |
+ // this large grain merge. See http://crbug.com/338484. |
+ |
+ // Done if no inputs to merge. |
+ if (buffer_queues.empty()) |
+ return true; |
+ |
+ // Build a vector of iterators, one for each input, to traverse inputs. |
+ // The union of these iterators points to the set of candidate buffers |
+ // for being appended to |merged_buffers|. |
+ std::vector<StreamParser::BufferQueue::const_iterator> itrs; |
xhwang
2014/02/06 00:36:32
Use vector::vector(size_type n) to avoid expanding
wolenetz
2014/02/06 23:56:03
Done.
|
+ for (size_t i = 0; i < buffer_queues.size(); ++i) |
+ itrs.push_back(buffer_queues[i]->begin()); |
+ |
+ // |last_decode_timestamp| tracks the lower bound, if any, that all candidate |
+ // buffers must not be less than. If |merged_buffers| already has buffers, |
+ // initialize |last_decode_timestamp| to the decode timestamp of the last |
+ // buffer in it. |
+ base::TimeDelta last_decode_timestamp = kNoTimestamp(); |
+ if (!merged_buffers->empty()) |
+ last_decode_timestamp = merged_buffers->back()->GetDecodeTimestamp(); |
+ |
+ // Repeatedly select and append the next buffer from the candidate buffers |
+ // until either: |
+ // 1) returning false, to indicate detection of decreasing DTS in some queue, |
+ // when a candidate buffer has decode timestamp below |
+ // |last_decode_timestamp|, which means either an input buffer wasn't |
+ // sorted correctly or had a buffer with decode timestamp below the last |
+ // buffer, if any, in |merged_buffers|, or |
+ // 2) returning true when all buffers have been merged successfully; |
+ // equivalently, when all of the iterators in |itrs| have reached the end |
+ // of their respective queue from |buffer_queues|. |
+ while (true) { |
+ // Tracks which queue's iterator is pointing to the candidate buffer to |
+ // append next, or -1 if no candidate buffers found. This indexes |itrs|. |
+ int index_of_queue_with_next_decode_timestamp = -1; |
+ base::TimeDelta next_decode_timestamp = kNoTimestamp(); |
+ |
+ // Scan each of the iterators for |buffer_queues| to find the candidate |
+ // buffer, if any, that has the lowest decode timestamp. |
+ for (size_t i = 0; i < buffer_queues.size(); ++i) { |
+ if (itrs[i] != buffer_queues[i]->end()) { |
xhwang
2014/02/06 00:36:32
if (itrs[i] == buffer_queues[i]->end())
continue
wolenetz
2014/02/06 23:56:03
Done.
|
+ // Extract the candidate buffer's decode timestamp. |
+ base::TimeDelta ts = (*itrs[i])->GetDecodeTimestamp(); |
+ |
+ if (last_decode_timestamp != kNoTimestamp() && |
+ ts < last_decode_timestamp) |
+ return false; |
+ |
+ if (ts < next_decode_timestamp || |
+ next_decode_timestamp == kNoTimestamp()) { |
+ // Remember the decode timestamp and queue iterator index for this |
+ // potentially winning candidate buffer. |
+ next_decode_timestamp = ts; |
+ index_of_queue_with_next_decode_timestamp = i; |
+ } |
+ } |
+ } |
xhwang
2014/02/06 00:36:32
This block is inefficient. Ideally, we should use
wolenetz
2014/02/06 23:56:03
Done.
|
+ |
+ // All done if no further candidate buffers exist. |
+ if (index_of_queue_with_next_decode_timestamp == -1) |
+ return true; |
+ |
+ // Otherwise, append the winning candidate buffer to |merged_buffers|, |
+ // remember its decode timestamp as |last_decode_timestamp| now that it is |
+ // the last buffer in |merged_buffers|, advance the corresponding |
+ // input BufferQueue iterator, and continue. |
+ scoped_refptr<StreamParserBuffer> buffer = |
+ *itrs[index_of_queue_with_next_decode_timestamp]; |
+ last_decode_timestamp = buffer->GetDecodeTimestamp(); |
+ merged_buffers->push_back(buffer); |
+ ++itrs[index_of_queue_with_next_decode_timestamp]; |
+ } |
+} |
+ |
+bool MergeBufferQueues(const StreamParser::BufferQueue& audio_buffers, |
+ const StreamParser::BufferQueue& video_buffers, |
+ const StreamParser::TextBufferQueueMap& text_buffers, |
+ StreamParser::BufferQueue* merged_buffers) { |
+ DCHECK(merged_buffers); |
+ |
+ // Prepare vector containing pointers to any provided non-empty buffer queues. |
+ std::vector<const StreamParser::BufferQueue*> buffer_queues; |
+ if (!audio_buffers.empty()) |
+ buffer_queues.push_back(&audio_buffers); |
+ if (!video_buffers.empty()) |
+ buffer_queues.push_back(&video_buffers); |
+ for (StreamParser::TextBufferQueueMap::const_iterator map_itr = |
+ text_buffers.begin(); |
+ map_itr != text_buffers.end(); |
+ map_itr++) { |
+ if (!map_itr->second.empty()) |
+ buffer_queues.push_back(&(map_itr->second)); |
+ } |
+ |
+ // Do the merge. |
+ return MergeBufferQueuesInternal(buffer_queues, merged_buffers); |
+} |
+ |
} // namespace media |