Shaka Packager SDK
trick_play_handler.cc
1 // Copyright 2017 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/trick_play/trick_play_handler.h>
8 
9 #include <absl/log/check.h>
10 #include <absl/log/log.h>
11 
12 #include <packager/media/base/video_stream_info.h>
13 #include <packager/status.h>
14 
15 namespace shaka {
16 namespace media {
17 namespace {
18 const size_t kStreamIndexIn = 0;
19 const size_t kStreamIndexOut = 0;
20 } // namespace
21 
22 TrickPlayHandler::TrickPlayHandler(uint32_t factor) : factor_(factor) {
23  DCHECK_GE(factor, 1u)
24  << "Trick Play Handles must have a factor of 1 or higher.";
25 }
26 
27 Status TrickPlayHandler::InitializeInternal() {
28  return Status::OK;
29 }
30 
31 Status TrickPlayHandler::Process(std::unique_ptr<StreamData> stream_data) {
32  DCHECK(stream_data);
33  DCHECK_EQ(stream_data->stream_index, kStreamIndexIn);
34 
35  switch (stream_data->stream_data_type) {
36  case StreamDataType::kStreamInfo:
37  return OnStreamInfo(*stream_data->stream_info);
38 
39  case StreamDataType::kSegmentInfo:
40  return OnSegmentInfo(std::move(stream_data->segment_info));
41 
42  case StreamDataType::kMediaSample:
43  return OnMediaSample(*stream_data->media_sample);
44 
45  case StreamDataType::kCueEvent:
46  // Add the cue event to be dispatched later.
47  delayed_messages_.push_back(std::move(stream_data));
48  return Status::OK;
49 
50  default:
51  return Status(error::TRICK_PLAY_ERROR,
52  "Trick play only supports stream info, segment info, and "
53  "media sample messages.");
54  }
55 }
56 
57 Status TrickPlayHandler::OnFlushRequest(size_t input_stream_index) {
58  DCHECK_EQ(input_stream_index, 0u);
59 
60  // Send everything out in its "as-is" state as we no longer need to update
61  // anything.
62  Status s;
63  while (s.ok() && delayed_messages_.size()) {
64  s.Update(Dispatch(std::move(delayed_messages_.front())));
65  delayed_messages_.pop_front();
66  }
67 
68  return s.ok() ? MediaHandler::FlushAllDownstreams() : s;
69 }
70 
71 Status TrickPlayHandler::OnStreamInfo(const StreamInfo& info) {
72  if (info.stream_type() != kStreamVideo) {
73  return Status(error::TRICK_PLAY_ERROR,
74  "Trick play does not support non-video stream");
75  }
76 
77  // Copy the video so we can edit it. Set play back rate to be zero. It will be
78  // updated later before being dispatched downstream.
79  video_info_ = std::make_shared<VideoStreamInfo>(
80  static_cast<const VideoStreamInfo&>(info));
81 
82  if (video_info_->trick_play_factor() > 0) {
83  return Status(error::TRICK_PLAY_ERROR,
84  "This stream is already a trick play stream.");
85  }
86 
87  video_info_->set_trick_play_factor(factor_);
88  video_info_->set_playback_rate(0);
89 
90  // Add video info to the message queue so that it can be sent out with all
91  // other messages. It won't be sent until the second trick play frame comes
92  // through. Until then, it can be updated via the |video_info_| member.
93  delayed_messages_.push_back(
94  StreamData::FromStreamInfo(kStreamIndexOut, video_info_));
95 
96  return Status::OK;
97 }
98 
99 Status TrickPlayHandler::OnSegmentInfo(
100  std::shared_ptr<const SegmentInfo> info) {
101  if (delayed_messages_.empty()) {
102  return Status(error::TRICK_PLAY_ERROR,
103  "Cannot handle segments with no preceding samples.");
104  }
105 
106  // Trick play does not care about sub segments, only full segments matter.
107  if (info->is_subsegment) {
108  return Status::OK;
109  }
110 
111  const StreamDataType previous_type =
112  delayed_messages_.back()->stream_data_type;
113 
114  switch (previous_type) {
115  case StreamDataType::kSegmentInfo:
116  // In the case that there was an empty segment (no trick frame between in
117  // a segment) extend the previous segment to include the empty segment to
118  // avoid holes.
119  previous_segment_->duration += info->duration;
120  return Status::OK;
121 
122  case StreamDataType::kMediaSample:
123  // The segment has ended and there are media samples in the segment.
124  // Add the segment info to the list of delayed messages. Segment info will
125  // not get sent downstream until the next trick play frame comes through
126  // or flush is called.
127  previous_segment_ = std::make_shared<SegmentInfo>(*info);
128  delayed_messages_.push_back(
129  StreamData::FromSegmentInfo(kStreamIndexOut, previous_segment_));
130  return Status::OK;
131 
132  default:
133  return Status(error::TRICK_PLAY_ERROR,
134  "Unexpected sample in trick play deferred queue : type=" +
135  std::to_string(static_cast<int>(previous_type)));
136  }
137 }
138 
139 Status TrickPlayHandler::OnMediaSample(const MediaSample& sample) {
140  total_frames_++;
141 
142  if (sample.is_key_frame()) {
143  total_key_frames_++;
144 
145  if ((total_key_frames_ - 1) % factor_ == 0) {
146  return OnTrickFrame(sample);
147  }
148  }
149  // If the frame is not a trick play frame, then take the duration of this
150  // frame and add it to the previous trick play frame so that it will span the
151  // gap created by not passing this frame through.
152  DCHECK(previous_trick_frame_);
153  previous_trick_frame_->set_duration(previous_trick_frame_->duration() +
154  sample.duration());
155 
156  return Status::OK;
157 }
158 
159 Status TrickPlayHandler::OnTrickFrame(const MediaSample& sample) {
160  total_trick_frames_++;
161 
162  // Make a message we can store until later.
163  previous_trick_frame_ = sample.Clone();
164 
165  // Add the message to our queue so that it will be ready to go out.
166  delayed_messages_.push_back(
167  StreamData::FromMediaSample(kStreamIndexOut, previous_trick_frame_));
168 
169  // We need two trick play frames before we can send out our stream info, so we
170  // cannot send this media sample until after we send our sample info
171  // downstream.
172  if (total_trick_frames_ < 2) {
173  return Status::OK;
174  }
175 
176  // Update this now as it may be sent out soon via the delay message queue.
177  if (total_trick_frames_ == 2) {
178  // At this point, video_info will be at the head of the delay message queue
179  // and can still be updated safely.
180 
181  // The play back rate is determined by the number of frames between the
182  // first two trick play frames. The first trick play frame will be the
183  // first frame in the video.
184  video_info_->set_playback_rate(total_frames_ - 1);
185  }
186 
187  // Send out all delayed messages up until the new trick play frame we just
188  // added.
189  Status s;
190  while (s.ok() && delayed_messages_.size() > 1) {
191  s.Update(Dispatch(std::move(delayed_messages_.front())));
192  delayed_messages_.pop_front();
193  }
194  return s;
195 }
196 
197 } // namespace media
198 } // namespace shaka
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66