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