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