| // Copyright (c) 2012 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/base/stream_parser.h" |
| |
| #include "media/base/stream_parser_buffer.h" |
| |
| namespace media { |
| |
| StreamParser::InitParameters::InitParameters(base::TimeDelta duration) |
| : duration(duration), |
| liveness(DemuxerStream::LIVENESS_UNKNOWN), |
| detected_audio_track_count(0), |
| detected_video_track_count(0), |
| detected_text_track_count(0) {} |
| |
| StreamParser::StreamParser() = default; |
| |
| StreamParser::~StreamParser() = default; |
| |
| // Default implementation of ProcessChunks() is not fully implemented. |
| bool StreamParser::ProcessChunks(std::unique_ptr<BufferQueue> buffer_queue) { |
| NOTIMPLEMENTED(); // Likely the wrong type of parser is being used. |
| return false; |
| } |
| |
| 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|. |
| size_t num_itrs = buffer_queues.size(); |
| std::vector<StreamParser::BufferQueue::const_iterator> itrs(num_itrs); |
| for (size_t i = 0; i < num_itrs; ++i) |
| itrs[i] = 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. |
| DecodeTimestamp last_decode_timestamp = kNoDecodeTimestamp(); |
| 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|. |
| // TODO(wolenetz/acolwell): Ideally, we would use a heap to store the head of |
| // all queues and pop the head with lowest decode timestamp in log(N) time. |
| // However, N will typically be small and usage of this implementation is |
| // meant to be short-term. See http://crbug.com/338484. |
| 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; |
| DecodeTimestamp next_decode_timestamp = kNoDecodeTimestamp(); |
| |
| // 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 < num_itrs; ++i) { |
| if (itrs[i] == buffer_queues[i]->end()) |
| continue; |
| |
| // Extract the candidate buffer's decode timestamp. |
| DecodeTimestamp ts = (*itrs[i])->GetDecodeTimestamp(); |
| |
| if (last_decode_timestamp != kNoDecodeTimestamp() && |
| ts < last_decode_timestamp) |
| return false; |
| |
| if (ts < next_decode_timestamp || |
| next_decode_timestamp == kNoDecodeTimestamp()) { |
| // 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; |
| } |
| } |
| |
| // 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::BufferQueueMap& buffer_queue_map, |
| StreamParser::BufferQueue* merged_buffers) { |
| DCHECK(merged_buffers); |
| |
| // Prepare vector containing pointers to any provided non-empty buffer queues. |
| |
| // Append audio buffer queues first, before all other types, since |
| // FrameProcessorTest.AudioVideo_Discontinuity currently depends on audio |
| // buffers being processed first. |
| std::vector<const StreamParser::BufferQueue*> buffer_queues; |
| for (const auto& it : buffer_queue_map) { |
| DCHECK(!it.second.empty()); |
| if (it.second[0]->type() == DemuxerStream::AUDIO) |
| buffer_queues.push_back(&it.second); |
| } |
| for (const auto& it : buffer_queue_map) { |
| DCHECK(!it.second.empty()); |
| if (it.second[0]->type() != DemuxerStream::AUDIO) |
| buffer_queues.push_back(&it.second); |
| } |
| |
| // Do the merge. |
| return MergeBufferQueuesInternal(buffer_queues, merged_buffers); |
| } |
| |
| } // namespace media |