summaryrefslogtreecommitdiff
path: root/media/base/stream_parser.cc
blob: 9696c2a7dc94e32d9232da5e5de4e8ce1c27aa33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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/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|.
  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.
  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|.
  // 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;
    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 < num_itrs; ++i) {
      if (itrs[i] == buffer_queues[i]->end())
        continue;

      // 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;
      }
    }

    // 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