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