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