Shaka Packager SDK
Loading...
Searching...
No Matches
sync_point_queue.cc
1// Copyright 2018 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#include <packager/media/chunking/sync_point_queue.h>
8
9#include <iterator>
10#include <limits>
11#include <memory>
12#include <utility>
13
14#include <absl/log/check.h>
15#include <absl/synchronization/mutex.h>
16
17#include <packager/ad_cue_generator_params.h>
18#include <packager/media/base/media_handler.h>
19
20namespace shaka {
21namespace media {
22
23SyncPointQueue::SyncPointQueue(const AdCueGeneratorParams& params) {
24 for (const Cuepoint& point : params.cue_points) {
25 std::shared_ptr<CueEvent> event = std::make_shared<CueEvent>();
26 event->time_in_seconds = point.start_time_in_seconds;
27 unpromoted_[point.start_time_in_seconds] = std::move(event);
28 }
29}
30
32 absl::MutexLock lock(mutex_);
33 thread_count_++;
34}
35
37 {
38 absl::MutexLock lock(mutex_);
39 cancelled_ = true;
40 }
41 sync_condition_.SignalAll();
42}
43
44double SyncPointQueue::GetHint(double time_in_seconds) {
45 absl::MutexLock lock(mutex_);
46
47 auto iter = promoted_.upper_bound(time_in_seconds);
48 if (iter != promoted_.end())
49 return iter->first;
50
51 iter = unpromoted_.upper_bound(time_in_seconds);
52 if (iter != unpromoted_.end())
53 return iter->first;
54
55 // Use MAX DOUBLE as the fall back so that we can force all streams to run
56 // out all their samples even when there are no cues.
57 return std::numeric_limits<double>::max();
58}
59
60std::shared_ptr<const CueEvent> SyncPointQueue::GetNext(
61 double hint_in_seconds) {
62 absl::MutexLock lock(mutex_);
63 while (!cancelled_) {
64 // Find the promoted cue that would line up with our hint, which is the
65 // first cue that is not less than |hint_in_seconds|.
66 auto iter = promoted_.lower_bound(hint_in_seconds);
67 if (iter != promoted_.end()) {
68 return iter->second;
69 }
70
71 // Promote |hint_in_seconds| if everyone is waiting.
72 if (waiting_thread_count_ + 1 == thread_count_) {
73 std::shared_ptr<const CueEvent> cue = PromoteAtNoLocking(hint_in_seconds);
74 CHECK(cue);
75 return cue;
76 }
77
78 waiting_thread_count_++;
79 // This blocks until either a cue is promoted or all threads are blocked
80 // (in which case, the unpromoted cue at the hint will be self-promoted
81 // and returned - see section above). Spurious signal events are possible
82 // with most condition variable implementations, so if it returns, we go
83 // back and check if a cue is actually promoted or not.
84 sync_condition_.Wait(&mutex_);
85 waiting_thread_count_--;
86 }
87 return nullptr;
88}
89
90std::shared_ptr<const CueEvent> SyncPointQueue::PromoteAt(
91 double time_in_seconds) {
92 absl::MutexLock lock(mutex_);
93 return PromoteAtNoLocking(time_in_seconds);
94}
95
96bool SyncPointQueue::HasMore(double hint_in_seconds) const {
97 return hint_in_seconds < std::numeric_limits<double>::max();
98}
99
100std::shared_ptr<const CueEvent> SyncPointQueue::PromoteAtNoLocking(
101 double time_in_seconds) {
102 mutex_.AssertHeld();
103
104 // It is possible that |time_in_seconds| has been promoted.
105 auto iter = promoted_.find(time_in_seconds);
106 if (iter != promoted_.end())
107 return iter->second;
108
109 // Find the unpromoted cue that would work for the given time, which is the
110 // first cue that is not greater than |time_in_seconds|.
111 // So find the the first cue that is greater than |time_in_seconds| first and
112 // then get the previous one.
113 iter = unpromoted_.upper_bound(time_in_seconds);
114 // The first cue in |unpromoted_| should not be greater than
115 // |time_in_seconds|. It could happen only if it has been promoted at a
116 // different timestamp, which can only be the result of unaligned GOPs.
117 if (iter == unpromoted_.begin())
118 return nullptr;
119 auto prev_iter = std::prev(iter);
120 DCHECK(prev_iter != unpromoted_.end());
121
122 std::shared_ptr<CueEvent> cue = prev_iter->second;
123 cue->time_in_seconds = time_in_seconds;
124
125 promoted_[time_in_seconds] = cue;
126 // Remove all unpromoted cues up to the cue that was just promoted.
127 // User may provide multiple cue points at the same or similar timestamps. The
128 // extra unused cues are simply ignored.
129 unpromoted_.erase(unpromoted_.begin(), iter);
130
131 // Wake up other threads that may be waiting.
132 sync_condition_.SignalAll();
133 return cue;
134}
135
136} // namespace media
137} // namespace shaka
std::shared_ptr< const CueEvent > PromoteAt(double time_in_seconds)
std::shared_ptr< const CueEvent > GetNext(double hint_in_seconds)
bool HasMore(double hint_in_seconds) const
void Cancel()
Cancel the queue and unblock all threads.
double GetHint(double time_in_seconds)
All the methods that are virtual are virtual for mocking.