1 // Copyright 2015 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.
5 #include "media/base/android/access_unit_queue.h"
7 #include "base/logging.h"
8 #include "base/stl_util.h"
9 #include "media/base/demuxer_stream.h"
14 // Amount of history chunks we keep by default. The zero size means we do not
15 // keep chunks before the current one and the history is limited by the size
17 const int kDefaultHistoryChunksAmount
= 0;
20 AccessUnitQueue::AccessUnitQueue()
22 history_chunks_amount_(kDefaultHistoryChunksAmount
),
24 current_chunk_
= chunks_
.end();
27 AccessUnitQueue::~AccessUnitQueue() {
28 STLDeleteContainerPointers(chunks_
.begin(), chunks_
.end());
31 void AccessUnitQueue::PushBack(const DemuxerData
& data
) {
33 DCHECK(!data
.access_units
.empty());
36 // If there is an AU with |kConfigChanged| status, it must be the last
37 // AU in the chunk and the data should have exactly one corresponding
39 for (size_t i
= 0; i
< data
.access_units
.size(); ++i
) {
40 const AccessUnit
& unit
= data
.access_units
[i
];
42 // EOS must be the last unit in the chunk.
43 if (unit
.is_end_of_stream
) {
44 DCHECK(i
== data
.access_units
.size() - 1);
47 // kConfigChanged must be the last unit in the chunk.
48 if (unit
.status
== DemuxerStream::kConfigChanged
) {
49 DCHECK(i
== data
.access_units
.size() - 1);
50 DCHECK(data
.demuxer_configs
.size() == 1);
53 if (unit
.status
== DemuxerStream::kAborted
) {
54 DVLOG(1) << "AccessUnitQueue::" << __FUNCTION__
<< " kAborted";
59 // Create the next chunk and copy data to it.
60 DemuxerData
* chunk
= new DemuxerData(data
);
62 // EOS flag can only be in the last access unit.
63 bool has_eos
= chunk
->access_units
.back().is_end_of_stream
;
65 // Append this chunk to the queue.
66 base::AutoLock
lock(lock_
);
68 // Ignore the input after we have received EOS.
74 bool was_empty
= (current_chunk_
== chunks_
.end());
76 // The container |chunks_| will own the chunk.
77 chunks_
.push_back(chunk
);
79 // Position the current chunk.
81 current_chunk_
= --chunks_
.end();
85 // We expect that the chunk containing EOS is the last chunk.
90 void AccessUnitQueue::Advance() {
92 base::AutoLock
lock(lock_
);
94 if (current_chunk_
== chunks_
.end())
98 if (index_in_chunk_
< (*current_chunk_
)->access_units
.size())
104 // Keep only |history_chunks_amount_| before the current one.
105 // std::distance() and std::advance() do not work efficiently with std::list,
106 // but the history_size should be small (default is 0).
107 size_t num_consumed_chunks
= std::distance(chunks_
.begin(), current_chunk_
);
108 if (num_consumed_chunks
> history_chunks_amount_
) {
109 DataChunkQueue::iterator first_to_keep
= chunks_
.begin();
110 std::advance(first_to_keep
, num_consumed_chunks
- history_chunks_amount_
);
111 STLDeleteContainerPointers(chunks_
.begin(), first_to_keep
);
112 chunks_
.erase(chunks_
.begin(), first_to_keep
);
116 void AccessUnitQueue::Flush() {
118 base::AutoLock
lock(lock_
);
120 STLDeleteContainerPointers(chunks_
.begin(), chunks_
.end());
123 current_chunk_
= chunks_
.end();
128 bool AccessUnitQueue::RewindToLastKeyFrame() {
130 base::AutoLock
lock(lock_
);
132 // Search for the key frame backwards. Start with the current AU.
134 // Start with current chunk.
135 if (current_chunk_
!= chunks_
.end()) {
136 for (int i
= (int)index_in_chunk_
; i
>= 0; --i
) {
137 if ((*current_chunk_
)->access_units
[i
].is_key_frame
) {
144 // Position reverse iterator before the current chunk.
145 DataChunkQueue::reverse_iterator
rchunk(current_chunk_
);
147 for (; rchunk
!= chunks_
.rend(); ++rchunk
) {
148 int i
= (int)(*rchunk
)->access_units
.size() - 1;
149 for (; i
>= 0; --i
) {
150 if ((*rchunk
)->access_units
[i
].is_key_frame
) {
152 current_chunk_
= --rchunk
.base();
161 AccessUnitQueue::Info
AccessUnitQueue::GetInfo() const {
162 // Media thread, Decoder thread
165 base::AutoLock
lock(lock_
);
167 GetUnconsumedAccessUnitLength(&info
.length
, &info
.data_length
);
169 info
.has_eos
= has_eos_
;
170 info
.front_unit
= nullptr;
171 info
.configs
= nullptr;
173 if (info
.length
> 0) {
174 DCHECK(current_chunk_
!= chunks_
.end());
175 DCHECK(index_in_chunk_
< (*current_chunk_
)->access_units
.size());
176 info
.front_unit
= &(*current_chunk_
)->access_units
[index_in_chunk_
];
178 if (info
.front_unit
->status
== DemuxerStream::kConfigChanged
) {
179 DCHECK((*current_chunk_
)->demuxer_configs
.size() == 1);
180 info
.configs
= &(*current_chunk_
)->demuxer_configs
[0];
186 void AccessUnitQueue::SetHistorySizeForTesting(size_t history_chunks_amount
) {
187 history_chunks_amount_
= history_chunks_amount
;
190 void AccessUnitQueue::GetUnconsumedAccessUnitLength(int* total_length
,
191 int* data_length
) const {
192 *total_length
= *data_length
= 0;
194 DataChunkQueue::const_iterator chunk
;
195 for (chunk
= current_chunk_
; chunk
!= chunks_
.end(); ++chunk
) {
196 size_t chunk_size
= (*chunk
)->access_units
.size();
197 *total_length
+= chunk_size
;
198 *data_length
+= chunk_size
;
200 // Do not count configuration changes for |data_length|.
201 if (!(*chunk
)->demuxer_configs
.empty()) {
202 DCHECK((*chunk
)->demuxer_configs
.size() == 1);
207 *total_length
-= index_in_chunk_
;
208 *data_length
-= index_in_chunk_
;