OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/base/stream_parser.h" | 5 #include "media/base/stream_parser.h" |
6 | 6 |
| 7 #include "media/base/buffers.h" |
| 8 #include "media/base/stream_parser_buffer.h" |
| 9 |
7 namespace media { | 10 namespace media { |
8 | 11 |
9 StreamParser::StreamParser() {} | 12 StreamParser::StreamParser() {} |
10 | 13 |
11 StreamParser::~StreamParser() {} | 14 StreamParser::~StreamParser() {} |
12 | 15 |
| 16 static bool MergeBufferQueuesInternal( |
| 17 const std::vector<const StreamParser::BufferQueue*>& buffer_queues, |
| 18 StreamParser::BufferQueue* merged_buffers) { |
| 19 // Instead of std::merge usage, this method implements a custom merge because: |
| 20 // 1) |buffer_queues| may contain N queues, |
| 21 // 2) we must detect and return false if any of the queues in |buffer_queues| |
| 22 // is unsorted, and |
| 23 // 3) we must detect and return false if any of the buffers in |buffer_queues| |
| 24 // has a decode timestamp prior to the last, if any, buffer in |
| 25 // |merged_buffers|. |
| 26 // TODO(wolenetz/acolwell): Refactor stream parsers to eliminate need for |
| 27 // this large grain merge. See http://crbug.com/338484. |
| 28 |
| 29 // Done if no inputs to merge. |
| 30 if (buffer_queues.empty()) |
| 31 return true; |
| 32 |
| 33 // Build a vector of iterators, one for each input, to traverse inputs. |
| 34 // The union of these iterators points to the set of candidate buffers |
| 35 // for being appended to |merged_buffers|. |
| 36 size_t num_itrs = buffer_queues.size(); |
| 37 std::vector<StreamParser::BufferQueue::const_iterator> itrs(num_itrs); |
| 38 for (size_t i = 0; i < num_itrs; ++i) |
| 39 itrs[i] = buffer_queues[i]->begin(); |
| 40 |
| 41 // |last_decode_timestamp| tracks the lower bound, if any, that all candidate |
| 42 // buffers must not be less than. If |merged_buffers| already has buffers, |
| 43 // initialize |last_decode_timestamp| to the decode timestamp of the last |
| 44 // buffer in it. |
| 45 base::TimeDelta last_decode_timestamp = kNoTimestamp(); |
| 46 if (!merged_buffers->empty()) |
| 47 last_decode_timestamp = merged_buffers->back()->GetDecodeTimestamp(); |
| 48 |
| 49 // Repeatedly select and append the next buffer from the candidate buffers |
| 50 // until either: |
| 51 // 1) returning false, to indicate detection of decreasing DTS in some queue, |
| 52 // when a candidate buffer has decode timestamp below |
| 53 // |last_decode_timestamp|, which means either an input buffer wasn't |
| 54 // sorted correctly or had a buffer with decode timestamp below the last |
| 55 // buffer, if any, in |merged_buffers|, or |
| 56 // 2) returning true when all buffers have been merged successfully; |
| 57 // equivalently, when all of the iterators in |itrs| have reached the end |
| 58 // of their respective queue from |buffer_queues|. |
| 59 // TODO(wolenetz/acolwell): Ideally, we would use a heap to store the head of |
| 60 // all queues and pop the head with lowest decode timestamp in log(N) time. |
| 61 // However, N will typically be small and usage of this implementation is |
| 62 // meant to be short-term. See http://crbug.com/338484. |
| 63 while (true) { |
| 64 // Tracks which queue's iterator is pointing to the candidate buffer to |
| 65 // append next, or -1 if no candidate buffers found. This indexes |itrs|. |
| 66 int index_of_queue_with_next_decode_timestamp = -1; |
| 67 base::TimeDelta next_decode_timestamp = kNoTimestamp(); |
| 68 |
| 69 // Scan each of the iterators for |buffer_queues| to find the candidate |
| 70 // buffer, if any, that has the lowest decode timestamp. |
| 71 for (size_t i = 0; i < num_itrs; ++i) { |
| 72 if (itrs[i] == buffer_queues[i]->end()) |
| 73 continue; |
| 74 |
| 75 // Extract the candidate buffer's decode timestamp. |
| 76 base::TimeDelta ts = (*itrs[i])->GetDecodeTimestamp(); |
| 77 |
| 78 if (last_decode_timestamp != kNoTimestamp() && |
| 79 ts < last_decode_timestamp) |
| 80 return false; |
| 81 |
| 82 if (ts < next_decode_timestamp || |
| 83 next_decode_timestamp == kNoTimestamp()) { |
| 84 // Remember the decode timestamp and queue iterator index for this |
| 85 // potentially winning candidate buffer. |
| 86 next_decode_timestamp = ts; |
| 87 index_of_queue_with_next_decode_timestamp = i; |
| 88 } |
| 89 } |
| 90 |
| 91 // All done if no further candidate buffers exist. |
| 92 if (index_of_queue_with_next_decode_timestamp == -1) |
| 93 return true; |
| 94 |
| 95 // Otherwise, append the winning candidate buffer to |merged_buffers|, |
| 96 // remember its decode timestamp as |last_decode_timestamp| now that it is |
| 97 // the last buffer in |merged_buffers|, advance the corresponding |
| 98 // input BufferQueue iterator, and continue. |
| 99 scoped_refptr<StreamParserBuffer> buffer = |
| 100 *itrs[index_of_queue_with_next_decode_timestamp]; |
| 101 last_decode_timestamp = buffer->GetDecodeTimestamp(); |
| 102 merged_buffers->push_back(buffer); |
| 103 ++itrs[index_of_queue_with_next_decode_timestamp]; |
| 104 } |
| 105 } |
| 106 |
| 107 bool MergeBufferQueues(const StreamParser::BufferQueue& audio_buffers, |
| 108 const StreamParser::BufferQueue& video_buffers, |
| 109 const StreamParser::TextBufferQueueMap& text_buffers, |
| 110 StreamParser::BufferQueue* merged_buffers) { |
| 111 DCHECK(merged_buffers); |
| 112 |
| 113 // Prepare vector containing pointers to any provided non-empty buffer queues. |
| 114 std::vector<const StreamParser::BufferQueue*> buffer_queues; |
| 115 if (!audio_buffers.empty()) |
| 116 buffer_queues.push_back(&audio_buffers); |
| 117 if (!video_buffers.empty()) |
| 118 buffer_queues.push_back(&video_buffers); |
| 119 for (StreamParser::TextBufferQueueMap::const_iterator map_itr = |
| 120 text_buffers.begin(); |
| 121 map_itr != text_buffers.end(); |
| 122 map_itr++) { |
| 123 if (!map_itr->second.empty()) |
| 124 buffer_queues.push_back(&(map_itr->second)); |
| 125 } |
| 126 |
| 127 // Do the merge. |
| 128 return MergeBufferQueuesInternal(buffer_queues, merged_buffers); |
| 129 } |
| 130 |
13 } // namespace media | 131 } // namespace media |
OLD | NEW |