Shaka Player Embedded
streams.cc
Go to the documentation of this file.
1 // Copyright 2017 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "shaka/media/streams.h"
16 
17 #include <glog/logging.h>
18 #include <stdio.h>
19 
20 #include <algorithm>
21 #include <cmath>
22 #include <iterator>
23 #include <list>
24 #include <unordered_set>
25 #include <utility>
26 
27 #include "src/debug/mutex.h"
28 #include "src/media/media_utils.h"
29 #include "src/util/macros.h"
30 
31 namespace shaka {
32 namespace media {
33 
34 namespace {
35 
36 // These exist to reduce the number of if checks for ordering. This is done
37 // by using a single if at the beginning of the method and using a function
38 // pointer to one of these methods.
39 
40 template <bool OrderByDts = true>
41 double GetTime(const std::shared_ptr<BaseFrame>& a) {
42  return a->dts;
43 }
44 template <>
45 double GetTime<false>(const std::shared_ptr<BaseFrame>& a) {
46  return a->pts;
47 }
48 
49 #ifndef NDEBUG
50 template <bool OrderByDts>
51 bool FrameLessThan(const std::shared_ptr<BaseFrame>& a,
52  const std::shared_ptr<BaseFrame>& b) {
53  return GetTime<OrderByDts>(a) < GetTime<OrderByDts>(b);
54 }
55 #endif
56 
57 template <bool OrderByDts>
58 bool FrameExtendsPast(const std::shared_ptr<BaseFrame>& a,
59  const std::shared_ptr<BaseFrame>& b) {
60  return GetTime<OrderByDts>(a) + a->duration + StreamBase::kMaxGapSize >=
61  GetTime<OrderByDts>(b);
62 }
63 
64 template <typename Iter>
65 void UpdatePtsRanges(Iter range) {
66  DCHECK(!range->frames.empty());
67  range->start_pts = HUGE_VAL;
68  range->end_pts = -HUGE_VAL;
69  for (auto& frame : range->frames) {
70  range->start_pts = std::min(range->start_pts, frame->pts);
71  range->end_pts = std::max(range->end_pts, frame->pts + frame->duration);
72  }
73 }
74 
90 template <bool OrderByDts>
91 std::list<std::shared_ptr<BaseFrame>>::iterator FrameLowerBound(
92  const std::list<std::shared_ptr<BaseFrame>>& list, double time) {
93  auto& mutable_list =
94  const_cast<std::list<std::shared_ptr<BaseFrame>>&>(list); // NOLINT
95  if (list.empty())
96  return mutable_list.end();
97 
98  if (time - GetTime<OrderByDts>(list.front()) <
99  GetTime<OrderByDts>(list.back()) - time) {
100  // Closer to the front, find the first element that is not less than |time|.
101  auto notLessThan = [&time](const std::shared_ptr<BaseFrame>& other) {
102  return GetTime<OrderByDts>(other) >= time;
103  };
104  return std::find_if(mutable_list.begin(), mutable_list.end(), notLessThan);
105  }
106 
107  // Closer to the end, first use a reverse search to find the first (in
108  // reverse order) that is less than |time|.
109  auto isLessThan = [&time](const std::shared_ptr<BaseFrame>& other) {
110  return GetTime<OrderByDts>(other) < time;
111  };
112  auto rit =
113  std::find_if(mutable_list.rbegin(), mutable_list.rend(), isLessThan);
114  // |*rit| is equal to the first element (going backwards) that is less than
115  // |frame|. We want to return |rit - 1| as a forward iterator, namely the
116  // first element that is not less than |frame|. The |base()| of a reverse
117  // iterator is the same element as |*(rit - 1)|, so return that.
118  // See: http://en.cppreference.com/w/cpp/iterator/reverse_iterator/base
119  return rit.base();
120 }
121 
122 struct Range {
123  Range() {}
124  ~Range() {}
125 
127 
128  std::list<std::shared_ptr<BaseFrame>> frames;
129 
130  double start_pts = HUGE_VAL;
131  double end_pts = -HUGE_VAL;
132 };
133 
134 } // namespace
135 
137  public:
138  explicit Impl(bool order_by_dts)
139  : mutex("StreamBase"), order_by_dts(order_by_dts) {}
140 
142  std::list<Range> buffered_ranges;
143  const bool order_by_dts;
144 };
145 
146 
147 StreamBase::StreamBase(bool order_by_dts) : impl_(new Impl(order_by_dts)) {}
148 
150 
151 size_t StreamBase::EstimateSize() const {
152  std::unique_lock<Mutex> lock(impl_->mutex);
153 
154  size_t size = 0;
155  for (auto& range : impl_->buffered_ranges) {
156  for (auto& frame : range.frames)
157  size += frame->EstimateSize();
158  }
159  return size;
160 }
161 
162 void StreamBase::AddFrameInternal(std::shared_ptr<BaseFrame> frame) {
163  std::unique_lock<Mutex> lock(impl_->mutex);
164  DCHECK(frame);
165 
166  auto extendsPast =
167  impl_->order_by_dts ? &FrameExtendsPast<true> : &FrameExtendsPast<false>;
168  auto lowerBound =
169  impl_->order_by_dts ? &FrameLowerBound<true> : &FrameLowerBound<false>;
170  auto getTime = impl_->order_by_dts ? &GetTime<true> : &GetTime<false>;
171 
172  // Find the first buffered range that ends after |frame|.
173  auto range_it =
174  std::find_if(impl_->buffered_ranges.begin(), impl_->buffered_ranges.end(),
175  [&](const Range& range) {
176  return extendsPast(range.frames.back(), frame);
177  });
178 
179  if (range_it == impl_->buffered_ranges.end()) {
180  // |frame| was after every existing range, create a new one.
181  impl_->buffered_ranges.emplace_back();
182  impl_->buffered_ranges.back().start_pts = frame->pts;
183  impl_->buffered_ranges.back().end_pts = frame->pts + frame->duration;
184  impl_->buffered_ranges.back().frames.emplace_back(frame);
185  } else if (!extendsPast(frame, range_it->frames.front())) {
186  // |frame| is before this range, so it starts a new range before this one.
187  auto it = impl_->buffered_ranges.emplace(range_it);
188  it->start_pts = frame->pts;
189  it->end_pts = frame->pts + frame->duration;
190  it->frames.emplace_back(frame);
191  } else {
192  // |frame| is inside the current range.
193  auto frame_it = lowerBound(range_it->frames, getTime(frame));
194  range_it->start_pts = std::min(range_it->start_pts, frame->pts);
195  range_it->end_pts =
196  std::max(range_it->end_pts, frame->pts + frame->duration);
197  if (frame_it != range_it->frames.end() &&
198  getTime(*frame_it) == getTime(frame)) {
199  swap(*frame_it, frame);
200  } else {
201  range_it->frames.insert(frame_it, frame);
202  }
203  }
204 
205  // If the frame closed a gap, then merge the buffered ranges.
206  DCHECK_NE(0u, impl_->buffered_ranges.size());
207  for (auto it = std::next(impl_->buffered_ranges.begin());
208  it != impl_->buffered_ranges.end();) {
209  auto prev = std::prev(it);
210  if (extendsPast(prev->frames.back(), it->frames.front())) {
211  // Move all elements from |it->frames| to the end of |prev->frames|.
212  // Since both lists are sorted and |prev < it|, this will remain sorted.
213  prev->frames.splice(prev->frames.end(), it->frames);
214  prev->start_pts = std::min(prev->start_pts, it->start_pts);
215  prev->end_pts = std::max(prev->end_pts, it->end_pts);
216  it = impl_->buffered_ranges.erase(it);
217  } else {
218  it++;
219  }
220  }
221 
222  AssertRangesSorted();
223 }
224 
225 std::vector<BufferedRange> StreamBase::GetBufferedRanges() const {
226  std::unique_lock<Mutex> lock(impl_->mutex);
227  AssertRangesSorted();
228 
229  std::vector<BufferedRange> ret;
230  ret.reserve(impl_->buffered_ranges.size());
231  for (const Range& range : impl_->buffered_ranges) {
232  ret.emplace_back(range.start_pts, range.end_pts);
233  }
234  return ret;
235 }
236 
237 size_t StreamBase::CountFramesBetween(double start_time,
238  double end_time) const {
239  std::unique_lock<Mutex> lock(impl_->mutex);
240  AssertRangesSorted();
241 
242  auto getTime = impl_->order_by_dts ? &GetTime<true> : &GetTime<false>;
243  auto lowerBound =
244  impl_->order_by_dts ? &FrameLowerBound<true> : &FrameLowerBound<false>;
245 
246  // Find the first buffered range that includes or is after |start_time|.
247  auto range_it =
248  std::find_if(impl_->buffered_ranges.begin(), impl_->buffered_ranges.end(),
249  [&](const Range& range) {
250  return getTime(range.frames.back()) >= start_time;
251  });
252 
253  size_t num_frames = 0;
254  for (; range_it != impl_->buffered_ranges.end(); range_it++) {
255  // |start| points to the frame that starts at or greater than
256  // |start_time|.
257  auto start = lowerBound(range_it->frames, start_time);
258  auto end = lowerBound(range_it->frames, end_time);
259  DCHECK(start != range_it->frames.end());
260  num_frames += std::distance(start, end);
261  if (getTime(*start) == start_time && start != end)
262  num_frames--;
263  if (end != range_it->frames.end())
264  break;
265  }
266 
267  return num_frames;
268 }
269 
270 void StreamBase::Remove(double start, double end) {
271  // Note that remove always uses PTS, even when sorting using DTS. This is
272  // intended to work like the MSE definition.
273 
274  std::unique_lock<Mutex> lock(impl_->mutex);
275  bool is_removing = false;
276  for (auto it = impl_->buffered_ranges.begin();
277  it != impl_->buffered_ranges.end();) {
278  // These represent the range of frames within this buffer to delete.
279  auto frame_del_start = is_removing ? it->frames.begin() : it->frames.end();
280  auto frame_del_end = it->frames.end();
281 
282  for (auto frame = it->frames.begin(); frame != it->frames.end(); frame++) {
283  if (!is_removing) {
284  // Only start deleting frames whose start time is in the range.
285  if ((*frame)->pts >= start && (*frame)->pts < end) {
286  is_removing = true;
287  frame_del_start = frame;
288  }
289  } else if ((*frame)->pts >= end && (*frame)->is_key_frame) {
290  // The MSE spec says to remove up to the next key frame.
291  frame_del_end = frame;
292  is_removing = false;
293  break;
294  }
295  }
296 
297  if (frame_del_start != it->frames.begin() &&
298  frame_del_start != it->frames.end() &&
299  frame_del_end != it->frames.end()) {
300  // We deleted a partial range, so we need to split the buffered range.
301  auto new_it = impl_->buffered_ranges.emplace(it); // new_it + 1 == it
302 
303  // Move the elements before |frame_del_start| from |it->frames| to
304  // |new_it->frames|.
305  new_it->frames.splice(new_it->frames.end(), it->frames,
306  it->frames.begin(), frame_del_start);
307 
308  it->frames.erase(it->frames.begin(), frame_del_end);
309  UpdatePtsRanges(it);
310  UpdatePtsRanges(new_it);
311  it++; // list::emplace() doesn't invalidate any iterators.
312  } else {
313  it->frames.erase(frame_del_start, frame_del_end);
314  if (it->frames.empty()) {
315  it = impl_->buffered_ranges.erase(it);
316  } else {
317  UpdatePtsRanges(it);
318  it++;
319  }
320  }
321  }
322 
323  AssertRangesSorted();
324 }
325 
327  std::unique_lock<Mutex> lock(impl_->mutex);
328  impl_->buffered_ranges.clear();
329 }
330 
331 void StreamBase::DebugPrint(bool all_frames) const {
332  std::unique_lock<Mutex> lock(impl_->mutex);
333  AssertRangesSorted();
334 
335  fprintf(stderr, "Stream order by %s:\n", impl_->order_by_dts ? "DTS" : "PTS");
336  if (impl_->buffered_ranges.empty())
337  fprintf(stderr, " Nothing buffered\n");
338  size_t range_i = 0;
339  for (const Range& range : impl_->buffered_ranges) {
340  fprintf(stderr, " Range[%zu, %zu frames]: %.2f-%.2f\n", range_i,
341  range.frames.size(), range.start_pts, range.end_pts);
342  if (all_frames) {
343  size_t frame_i = 0;
344  for (const auto& frame : range.frames) {
345  fprintf(stderr,
346  " Frame[%zu]: is_key_frame=%-5s, pts=%.2f, dts=%.2f\n",
347  frame_i, frame->is_key_frame ? "true" : "false", frame->pts,
348  frame->dts);
349  frame_i++;
350  }
351  }
352  range_i++;
353  }
354 }
355 
356 std::shared_ptr<BaseFrame> StreamBase::GetFrameInternal(
357  double time, FrameLocation kind) const {
358  std::unique_lock<Mutex> lock(impl_->mutex);
359  AssertRangesSorted();
360 
361  auto getTime = impl_->order_by_dts ? &GetTime<true> : &GetTime<false>;
362  auto lowerBound =
363  impl_->order_by_dts ? &FrameLowerBound<true> : &FrameLowerBound<false>;
364 
365  // Find the first buffered range that includes or is after |time|.
366  auto it = std::find_if(
367  impl_->buffered_ranges.begin(), impl_->buffered_ranges.end(),
368  [&](const Range& range) { return getTime(range.frames.back()) >= time; });
369 
370  if (it == impl_->buffered_ranges.end()) {
371  if (kind == FrameLocation::After || impl_->buffered_ranges.empty())
372  return nullptr;
373 
374  it = std::prev(impl_->buffered_ranges.end());
375  }
376 
377  // |frame_it| points to the frame that starts at or greater than |time|.
378  auto frame_it = lowerBound(it->frames, time);
379 
380  switch (kind) {
382  // Find the frame after |time|.
383  DCHECK(frame_it != it->frames.end());
384  if (getTime(*frame_it) > time)
385  return *frame_it;
386  else if (std::next(frame_it) != it->frames.end())
387  return *std::next(frame_it);
388  else if (std::next(it) != impl_->buffered_ranges.end())
389  return *std::next(it)->frames.begin();
390  else
391  return nullptr;
392 
393  case FrameLocation::Near: {
394  if (frame_it == it->frames.end())
395  return it->frames.back();
396 
397  // Find the frame before this to see if it is closer.
398  auto prev_it = frame_it;
399  if (frame_it != it->frames.begin())
400  prev_it = std::prev(frame_it);
401  else if (it != impl_->buffered_ranges.begin())
402  prev_it = std::prev(std::prev(it)->frames.end());
403 
404  double prev_diff = time - getTime(*prev_it) - (*prev_it)->duration;
405  double diff = getTime(*frame_it) - time;
406  return prev_diff < diff && diff != 0 ? *prev_it : *frame_it;
407  }
408 
410  if (frame_it == it->frames.end())
411  frame_it = std::prev(it->frames.end());
412  else if (frame_it != it->frames.begin() && getTime(*frame_it) > time)
413  frame_it--; // If frame_it is a future frame, move backward.
414 
415  DCHECK((*it->frames.begin())->is_key_frame);
416  while (!(*frame_it)->is_key_frame)
417  frame_it--;
418 
419  return getTime(*frame_it) <= time ? *frame_it : nullptr;
420  }
421 }
422 
423 void StreamBase::AssertRangesSorted() const {
424 #ifndef NDEBUG
425  auto frame_less_than =
426  impl_->order_by_dts ? &FrameLessThan<true> : &FrameLessThan<false>;
427  auto range_is_valid = [&](const Range& range) {
428  // A buffered range must:
429  // - Be non-empty.
430  // - Start with a key frame.
431  // - Have sorted frames.
432  CHECK(!range.frames.empty());
433  CHECK(range.frames.front()->is_key_frame);
434  CHECK_LE(range.start_pts, range.end_pts);
435  CHECK(std::is_sorted(range.frames.begin(), range.frames.end(),
436  frame_less_than));
437  return true;
438  };
439  auto range_less_than = [&](const Range& first, const Range& second) {
440  // Some versions of the standard library will perform debug checks that will
441  // call this with the same argument. Don't assert in this case.
442  if (&first == &second)
443  return false;
444 
445  // The ranges must not overlap.
446  if (first.start_pts < second.start_pts) {
447  CHECK_LT(first.end_pts, second.start_pts);
448  return true;
449  }
450  CHECK_GT(first.start_pts, second.end_pts);
451  return false;
452  };
453 
454  CHECK(std::all_of(impl_->buffered_ranges.begin(),
455  impl_->buffered_ranges.end(), range_is_valid));
456  CHECK(std::is_sorted(impl_->buffered_ranges.begin(),
457  impl_->buffered_ranges.end(), range_less_than));
458 #endif
459 }
460 
461 } // namespace media
462 } // namespace shaka
Impl(bool order_by_dts)
Definition: streams.cc:138
void AddFrameInternal(std::shared_ptr< BaseFrame > frame)
Definition: streams.cc:162
static constexpr const double kMaxGapSize
Definition: streams.h:76
std::shared_ptr< shaka::media::DecodedFrame > frame
size_t EstimateSize() const
Definition: streams.cc:151
void DebugPrint(bool all_frames) const
Definition: streams.cc:331
StreamBase(bool order_by_dts)
Definition: streams.cc:147
double end_pts
Definition: streams.cc:131
void swap(shared_lock< Mutex > &a, shared_lock< Mutex > &b)
Definition: shared_lock.h:161
#define SHAKA_NON_COPYABLE_TYPE(Type)
Definition: macros.h:43
std::list< Range > buffered_ranges
Definition: streams.cc:142
std::shared_ptr< BaseFrame > GetFrameInternal(double time, FrameLocation kind) const
Definition: streams.cc:356
double start_pts
Definition: streams.cc:130
std::vector< BufferedRange > GetBufferedRanges() const
Definition: streams.cc:225
std::list< std::shared_ptr< BaseFrame > > frames
Definition: streams.cc:128
void Remove(double start, double end)
Definition: streams.cc:270
size_t CountFramesBetween(double start, double end) const
Definition: streams.cc:237