- add sources.
[platform/framework/web/crosswalk.git] / src / media / base / seekable_buffer.cc
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/seekable_buffer.h"
6
7 #include <algorithm>
8
9 #include "base/logging.h"
10 #include "media/base/data_buffer.h"
11
12 namespace media {
13
14 SeekableBuffer::SeekableBuffer(int backward_capacity, int forward_capacity)
15     : current_buffer_offset_(0),
16       backward_capacity_(backward_capacity),
17       backward_bytes_(0),
18       forward_capacity_(forward_capacity),
19       forward_bytes_(0),
20       current_time_(kNoTimestamp()) {
21   current_buffer_ = buffers_.begin();
22 }
23
24 SeekableBuffer::~SeekableBuffer() {
25 }
26
27 void SeekableBuffer::Clear() {
28   buffers_.clear();
29   current_buffer_ = buffers_.begin();
30   current_buffer_offset_ = 0;
31   backward_bytes_ = 0;
32   forward_bytes_ = 0;
33   current_time_ = kNoTimestamp();
34 }
35
36 int SeekableBuffer::Read(uint8* data, int size) {
37   DCHECK(data);
38   return InternalRead(data, size, true, 0);
39 }
40
41 int SeekableBuffer::Peek(uint8* data, int size, int forward_offset) {
42   DCHECK(data);
43   return InternalRead(data, size, false, forward_offset);
44 }
45
46 bool SeekableBuffer::GetCurrentChunk(const uint8** data, int* size) const {
47   BufferQueue::iterator current_buffer = current_buffer_;
48   int current_buffer_offset = current_buffer_offset_;
49   // Advance position if we are in the end of the current buffer.
50   while (current_buffer != buffers_.end() &&
51          current_buffer_offset >= (*current_buffer)->data_size()) {
52     ++current_buffer;
53     current_buffer_offset = 0;
54   }
55   if (current_buffer == buffers_.end())
56     return false;
57   *data = (*current_buffer)->data() + current_buffer_offset;
58   *size = (*current_buffer)->data_size() - current_buffer_offset;
59   return true;
60 }
61
62 bool SeekableBuffer::Append(const scoped_refptr<DataBuffer>& buffer_in) {
63   if (buffers_.empty() && buffer_in->timestamp() != kNoTimestamp()) {
64     current_time_ = buffer_in->timestamp();
65   }
66
67   // Since the forward capacity is only used to check the criteria for buffer
68   // full, we always append data to the buffer.
69   buffers_.push_back(buffer_in);
70
71   // After we have written the first buffer, update |current_buffer_| to point
72   // to it.
73   if (current_buffer_ == buffers_.end()) {
74     DCHECK_EQ(0, forward_bytes_);
75     current_buffer_ = buffers_.begin();
76   }
77
78   // Update the |forward_bytes_| counter since we have more bytes.
79   forward_bytes_ += buffer_in->data_size();
80
81   // Advise the user to stop append if the amount of forward bytes exceeds
82   // the forward capacity. A false return value means the user should stop
83   // appending more data to this buffer.
84   if (forward_bytes_ >= forward_capacity_)
85     return false;
86   return true;
87 }
88
89 bool SeekableBuffer::Append(const uint8* data, int size) {
90   if (size > 0) {
91     scoped_refptr<DataBuffer> data_buffer = DataBuffer::CopyFrom(data, size);
92     return Append(data_buffer);
93   } else {
94     // Return true if we have forward capacity.
95     return forward_bytes_ < forward_capacity_;
96   }
97 }
98
99 bool SeekableBuffer::Seek(int32 offset) {
100   if (offset > 0)
101     return SeekForward(offset);
102   else if (offset < 0)
103     return SeekBackward(-offset);
104   return true;
105 }
106
107 bool SeekableBuffer::SeekForward(int size) {
108   // Perform seeking forward only if we have enough bytes in the queue.
109   if (size > forward_bytes_)
110     return false;
111
112   // Do a read of |size| bytes.
113   int taken = InternalRead(NULL, size, true, 0);
114   DCHECK_EQ(taken, size);
115   return true;
116 }
117
118 bool SeekableBuffer::SeekBackward(int size) {
119   if (size > backward_bytes_)
120     return false;
121   // Record the number of bytes taken.
122   int taken = 0;
123   // Loop until we taken enough bytes and rewind by the desired |size|.
124   while (taken < size) {
125     // |current_buffer_| can never be invalid when we are in this loop. It can
126     // only be invalid before any data is appended. The invalid case should be
127     // handled by checks before we enter this loop.
128     DCHECK(current_buffer_ != buffers_.end());
129
130     // We try to consume at most |size| bytes in the backward direction. We also
131     // have to account for the offset we are in the current buffer, so take the
132     // minimum between the two to determine the amount of bytes to take from the
133     // current buffer.
134     int consumed = std::min(size - taken, current_buffer_offset_);
135
136     // Decreases the offset in the current buffer since we are rewinding.
137     current_buffer_offset_ -= consumed;
138
139     // Increase the amount of bytes taken in the backward direction. This
140     // determines when to stop the loop.
141     taken += consumed;
142
143     // Forward bytes increases and backward bytes decreases by the amount
144     // consumed in the current buffer.
145     forward_bytes_ += consumed;
146     backward_bytes_ -= consumed;
147     DCHECK_GE(backward_bytes_, 0);
148
149     // The current buffer pointed by current iterator has been consumed. Move
150     // the iterator backward so it points to the previous buffer.
151     if (current_buffer_offset_ == 0) {
152       if (current_buffer_ == buffers_.begin())
153         break;
154       // Move the iterator backward.
155       --current_buffer_;
156       // Set the offset into the current buffer to be the buffer size as we
157       // are preparing for rewind for next iteration.
158       current_buffer_offset_ = (*current_buffer_)->data_size();
159     }
160   }
161
162   UpdateCurrentTime(current_buffer_, current_buffer_offset_);
163
164   DCHECK_EQ(taken, size);
165   return true;
166 }
167
168 void SeekableBuffer::EvictBackwardBuffers() {
169   // Advances the iterator until we hit the current pointer.
170   while (backward_bytes_ > backward_capacity_) {
171     BufferQueue::iterator i = buffers_.begin();
172     if (i == current_buffer_)
173       break;
174     scoped_refptr<DataBuffer> buffer = *i;
175     backward_bytes_ -= buffer->data_size();
176     DCHECK_GE(backward_bytes_, 0);
177
178     buffers_.erase(i);
179   }
180 }
181
182 int SeekableBuffer::InternalRead(uint8* data, int size,
183                                  bool advance_position,
184                                  int forward_offset) {
185   // Counts how many bytes are actually read from the buffer queue.
186   int taken = 0;
187
188   BufferQueue::iterator current_buffer = current_buffer_;
189   int current_buffer_offset = current_buffer_offset_;
190
191   int bytes_to_skip = forward_offset;
192   while (taken < size) {
193     // |current_buffer| is valid since the first time this buffer is appended
194     // with data.
195     if (current_buffer == buffers_.end())
196       break;
197
198     scoped_refptr<DataBuffer> buffer = *current_buffer;
199
200     int remaining_bytes_in_buffer =
201         buffer->data_size() - current_buffer_offset;
202
203     if (bytes_to_skip == 0) {
204       // Find the right amount to copy from the current buffer referenced by
205       // |buffer|. We shall copy no more than |size| bytes in total and each
206       // single step copied no more than the current buffer size.
207       int copied = std::min(size - taken, remaining_bytes_in_buffer);
208
209       // |data| is NULL if we are seeking forward, so there's no need to copy.
210       if (data)
211         memcpy(data + taken, buffer->data() + current_buffer_offset, copied);
212
213       // Increase total number of bytes copied, which regulates when to end this
214       // loop.
215       taken += copied;
216
217       // We have read |copied| bytes from the current buffer. Advances the
218       // offset.
219       current_buffer_offset += copied;
220     } else {
221       int skipped = std::min(remaining_bytes_in_buffer, bytes_to_skip);
222       current_buffer_offset += skipped;
223       bytes_to_skip -= skipped;
224     }
225
226     // The buffer has been consumed.
227     if (current_buffer_offset == buffer->data_size()) {
228       if (advance_position) {
229         // Next buffer may not have timestamp, so we need to update current
230         // timestamp before switching to the next buffer.
231         UpdateCurrentTime(current_buffer, current_buffer_offset);
232       }
233
234       BufferQueue::iterator next = current_buffer;
235       ++next;
236       // If we are at the last buffer, don't advance.
237       if (next == buffers_.end())
238         break;
239
240       // Advances the iterator.
241       current_buffer = next;
242       current_buffer_offset = 0;
243     }
244   }
245
246   if (advance_position) {
247     // We have less forward bytes and more backward bytes. Updates these
248     // counters by |taken|.
249     forward_bytes_ -= taken;
250     backward_bytes_ += taken;
251     DCHECK_GE(forward_bytes_, 0);
252     DCHECK(current_buffer_ != buffers_.end() || forward_bytes_ == 0);
253
254     current_buffer_ = current_buffer;
255     current_buffer_offset_ = current_buffer_offset;
256
257     UpdateCurrentTime(current_buffer_, current_buffer_offset_);
258     EvictBackwardBuffers();
259   }
260
261   return taken;
262 }
263
264 void SeekableBuffer::UpdateCurrentTime(BufferQueue::iterator buffer,
265                                        int offset) {
266   // Garbage values are unavoidable, so this check will remain.
267   if (buffer != buffers_.end() &&
268       (*buffer)->timestamp() != kNoTimestamp()) {
269     int64 time_offset = ((*buffer)->duration().InMicroseconds() * offset) /
270                         (*buffer)->data_size();
271
272     current_time_ = (*buffer)->timestamp() +
273                     base::TimeDelta::FromMicroseconds(time_offset);
274   }
275 }
276
277 }  // namespace media