Shaka Packager SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
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 
15 namespace {
16 const size_t kMainStreamIndex = 0;
17 }
18 
19 TrickPlayHandler::TrickPlayHandler() {}
20 
21 TrickPlayHandler::~TrickPlayHandler() {}
22 
23 void TrickPlayHandler::SetHandlerForMainStream(
24  std::shared_ptr<MediaHandler> handler) {
25  SetHandler(kMainStreamIndex, std::move(handler));
26 }
27 
28 void TrickPlayHandler::SetHandlerForTrickPlay(
29  uint32_t trick_play_factor,
30  std::shared_ptr<MediaHandler> handler) {
31  trick_play_factors_.push_back(trick_play_factor);
32  // Trick play streams start from index 1.
33  SetHandler(trick_play_factors_.size(), std::move(handler));
34 }
35 
37  if (!HasMainStream()) {
38  return Status(error::TRICK_PLAY_ERROR,
39  "Trick play does not have main stream");
40  }
41  if (trick_play_factors_.empty()) {
42  return Status(error::TRICK_PLAY_ERROR,
43  "Trick play factors are not specified.");
44  }
45  size_t num_trick_play_factors = trick_play_factors_.size();
46  cached_stream_data_.resize(num_trick_play_factors);
47  playback_rates_.resize(num_trick_play_factors, 0);
48 
49  return Status::OK;
50 }
51 
52 Status TrickPlayHandler::Process(std::unique_ptr<StreamData> stream_data) {
53  // The non-trick play stream is dispatched at index 0.
54  // The trick-play streams are dispatched to index 1, index 2 and so on.
55  DCHECK(stream_data);
56  DCHECK_EQ(stream_data->stream_index, 0u);
57 
58  std::unique_ptr<StreamData> copy(new StreamData);
59  *copy = *stream_data;
60  Status status = Dispatch(std::move(copy));
61  if (!status.ok()) {
62  return status;
63  }
64 
65  std::shared_ptr<StreamData> shared_stream_data(std::move(stream_data));
66 
67  if (shared_stream_data->stream_data_type == StreamDataType::kStreamInfo) {
68  if (shared_stream_data->stream_info->stream_type() != kStreamVideo) {
69  status.SetError(error::TRICK_PLAY_ERROR,
70  "Trick play does not support non-video stream");
71  return status;
72  }
73  const VideoStreamInfo& video_stream_info =
74  static_cast<const VideoStreamInfo&>(*shared_stream_data->stream_info);
75  if (video_stream_info.trick_play_factor() > 0) {
76  status.SetError(error::TRICK_PLAY_ERROR,
77  "This stream is alreay a trick play stream.");
78  return status;
79  }
80  }
81 
82  if (shared_stream_data->stream_data_type == StreamDataType::kSegmentInfo) {
83  for (auto& cached_data : cached_stream_data_) {
84  // It is possible that trick play stream has large frame duration that
85  // some segments in the main stream are skipped. To avoid empty segments,
86  // only cache SegementInfo with MediaSample before it.
87  if (!cached_data.empty() &&
88  cached_data.back()->stream_data_type == StreamDataType::kMediaSample)
89  cached_data.push_back(shared_stream_data);
90  }
91  return Status::OK;
92  }
93 
94  if (shared_stream_data->stream_data_type != StreamDataType::kMediaSample) {
95  // Non media sample stream data needs to be dispatched to every output
96  // stream. It is just cached in every queue until a new key frame comes or
97  // the stream is flushed.
98  for (size_t i = 0; i < cached_stream_data_.size(); ++i)
99  cached_stream_data_[i].push_back(shared_stream_data);
100  return Status::OK;
101  }
102 
103  if (shared_stream_data->media_sample->is_key_frame()) {
104  // For a new key frame, some of the trick play streams may include it.
105  // The cached data in those trick play streams will be processed.
106  DCHECK_EQ(trick_play_factors_.size(), cached_stream_data_.size());
107  for (size_t i = 0; i < cached_stream_data_.size(); ++i) {
108  uint32_t factor = trick_play_factors_[i];
109  if (total_key_frames_ % factor == 0) {
110  // Delay processing cached stream data until receiving the second key
111  // frame so that the GOP size could be derived.
112  if (!cached_stream_data_[i].empty() && total_key_frames_ > 0) {
113  // Num of frames between first two key frames in the trick play
114  // streams. Use this as the playback_rate.
115  if (playback_rates_[i] == 0)
116  playback_rates_[i] = total_frames_;
117 
118  Status status =
119  ProcessCachedStreamData(i + 1, &cached_stream_data_[i]);
120  if (!status.ok())
121  return status;
122  }
123  cached_stream_data_[i].push_back(shared_stream_data);
124  }
125  }
126 
127  total_key_frames_++;
128  }
129 
130  total_frames_++;
131  prev_sample_end_timestamp_ = shared_stream_data->media_sample->dts() +
132  shared_stream_data->media_sample->duration();
133 
134  return Status::OK;
135 }
136 
137 bool TrickPlayHandler::ValidateOutputStreamIndex(size_t stream_index) const {
138  // Output stream index should be less than the number of trick play
139  // streams + one original stream.
140  return stream_index <= trick_play_factors_.size();
141 };
142 
143 Status TrickPlayHandler::OnFlushRequest(size_t input_stream_index) {
144  DCHECK_EQ(input_stream_index, 0u)
145  << "Trick Play Handler should only have single input.";
146  for (size_t i = 0; i < cached_stream_data_.size(); ++i) {
147  LOG_IF(WARNING, playback_rates_[i] == 0)
148  << "Max playout rate for trick play factor " << trick_play_factors_[i]
149  << " is not determined. "
150  << "Specify it as total number of frames: " << total_frames_ << ".";
151  playback_rates_[i] = total_frames_;
152  ProcessCachedStreamData(i + 1, &cached_stream_data_[i]);
153  }
155 }
156 
157 bool TrickPlayHandler::HasMainStream() {
158  const auto& handlers = output_handlers();
159  const auto& main_stream_handler = handlers.find(kMainStreamIndex);
160  if (main_stream_handler == handlers.end()) {
161  return false;
162  }
163  return main_stream_handler->second.first != nullptr;
164 }
165 
166 Status TrickPlayHandler::ProcessCachedStreamData(
167  size_t output_stream_index,
168  std::deque<std::shared_ptr<StreamData>>* cached_stream_data) {
169  while (!cached_stream_data->empty()) {
170  Status status =
171  ProcessOneStreamData(output_stream_index, *cached_stream_data->front());
172  if (!status.ok()) {
173  return status;
174  }
175  cached_stream_data->pop_front();
176  }
177  return Status::OK;
178 }
179 
180 Status TrickPlayHandler::ProcessOneStreamData(size_t output_stream_index,
181  const StreamData& stream_data) {
182  size_t trick_play_index = output_stream_index - 1;
183  uint32_t trick_play_factor = trick_play_factors_[trick_play_index];
184  Status status;
185  switch (stream_data.stream_data_type) {
186  // trick_play_factor in StreamInfo should be modified.
187  case StreamDataType::kStreamInfo: {
188  const VideoStreamInfo& video_stream_info =
189  static_cast<const VideoStreamInfo&>(*stream_data.stream_info);
190  std::shared_ptr<VideoStreamInfo> trick_play_video_stream_info(
191  new VideoStreamInfo(video_stream_info));
192  trick_play_video_stream_info->set_trick_play_factor(trick_play_factor);
193  DCHECK_GT(playback_rates_[trick_play_index], 0u);
194  trick_play_video_stream_info->set_playback_rate(
195  playback_rates_[trick_play_index]);
196  status =
197  DispatchStreamInfo(output_stream_index, trick_play_video_stream_info);
198  break;
199  }
200  case StreamDataType::kMediaSample: {
201  if (stream_data.media_sample->is_key_frame()) {
202  std::shared_ptr<MediaSample> trick_play_media_sample =
203  MediaSample::CopyFrom(*(stream_data.media_sample));
204  trick_play_media_sample->set_duration(prev_sample_end_timestamp_ -
205  stream_data.media_sample->dts());
206  status =
207  DispatchMediaSample(output_stream_index, trick_play_media_sample);
208  }
209  break;
210  }
211  default:
212  std::unique_ptr<StreamData> copy(new StreamData(stream_data));
213  copy->stream_index = output_stream_index;
214  status = Dispatch(std::move(copy));
215  break;
216  }
217  return status;
218 }
219 
220 } // namespace media
221 } // namespace shaka
Status Dispatch(std::unique_ptr< StreamData > stream_data)
bool ValidateOutputStreamIndex(size_t stream_index) const override
Validate if the stream at the specified index actually exists.
Status FlushAllDownstreams()
Flush all connected downstreams.
void SetError(error::Code error_code, const std::string &error_message)
Definition: status.h:135
Status OnFlushRequest(size_t input_stream_index) override
Event handler for flush request at the specific input stream index.
Status DispatchStreamInfo(size_t stream_index, std::shared_ptr< const StreamInfo > stream_info)
Dispatch the stream info to downstream handlers.
Status DispatchMediaSample(size_t stream_index, std::shared_ptr< const MediaSample > media_sample)
Dispatch the media sample to downstream handlers.
static std::shared_ptr< MediaSample > CopyFrom(const uint8_t *data, size_t size, bool is_key_frame)
Definition: media_sample.cc:45
Holds video stream information.
Status SetHandler(size_t output_stream_index, std::shared_ptr< MediaHandler > handler)
Connect downstream handler at the specified output stream index.
Status Process(std::unique_ptr< StreamData > stream_data) override