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