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 <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
15namespace shaka {
16namespace media {
17namespace {
18const size_t kStreamIndexIn = 0;
19const size_t kStreamIndexOut = 0;
20} // namespace
21
22TrickPlayHandler::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
27Status TrickPlayHandler::InitializeInternal() {
28 return Status::OK;
29}
30
31Status 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
57Status 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
71Status 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
99Status 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
139Status 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
159Status 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.