Shaka Packager SDK
webvtt_output_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/formats/webvtt/webvtt_output_handler.h"
8 
9 #include "packager/base/logging.h"
10 #include "packager/file/file.h"
11 #include "packager/media/base/muxer_util.h"
12 #include "packager/media/formats/webvtt/webvtt_timestamp.h"
13 
14 namespace shaka {
15 namespace media {
16 void WebVttOutputHandler::WriteCue(const std::string& id,
17  uint64_t start_ms,
18  uint64_t end_ms,
19  const std::string& settings,
20  const std::string& payload) {
21  // Build a block of text that makes up the cue so that we can use a loop to
22  // write all the lines.
23  const std::string start = MsToWebVttTimestamp(start_ms);
24  const std::string end = MsToWebVttTimestamp(end_ms);
25 
26  // Ids are optional
27  if (id.length()) {
28  buffer_.append(id);
29  buffer_.append("\n"); // end of id
30  }
31 
32  buffer_.append(start);
33  buffer_.append(" --> ");
34  buffer_.append(end);
35 
36  // Settings are optional
37  if (settings.length()) {
38  buffer_.append(" ");
39  buffer_.append(settings);
40  }
41  buffer_.append("\n"); // end of time & settings
42 
43  buffer_.append(payload);
44  buffer_.append("\n"); // end of payload
45  buffer_.append("\n"); // end of cue
46 }
47 
48 Status WebVttOutputHandler::WriteSegmentToFile(const std::string& filename) {
49  // Need blank line between "WEBVTT" and the first cue
50  const std::string WEBVTT_HEADER = "WEBVTT\n\n";
51 
52  File* file = File::Open(filename.c_str(), "w");
53 
54  if (file == nullptr) {
55  return Status(error::FILE_FAILURE, "Failed to open " + filename);
56  }
57 
58  size_t written;
59  written = file->Write(WEBVTT_HEADER.c_str(), WEBVTT_HEADER.size());
60  if (written != WEBVTT_HEADER.size()) {
61  return Status(error::FILE_FAILURE, "Failed to write webvtt header to file");
62  }
63 
64  written = file->Write(buffer_.c_str(), buffer_.size());
65  if (written != buffer_.size()) {
66  return Status(error::FILE_FAILURE,
67  "Failed to write webvtt cotnent to file");
68  }
69 
70  // Since all the cues have been written to disk, there is no reason to hold
71  // onto that information anymore.
72  buffer_.clear();
73 
74  bool closed = file->Close();
75  if (!closed) {
76  return Status(error::FILE_FAILURE, "Failed to close " + filename);
77  }
78 
79  return Status::OK;
80 }
81 
82 Status WebVttOutputHandler::InitializeInternal() {
83  return Status::OK;
84 }
85 
86 Status WebVttOutputHandler::Process(std::unique_ptr<StreamData> stream_data) {
87  switch (stream_data->stream_data_type) {
88  case StreamDataType::kStreamInfo:
89  return OnStreamInfo(*stream_data->stream_info);
90  case StreamDataType::kSegmentInfo:
91  return OnSegmentInfo(*stream_data->segment_info);
92  case StreamDataType::kTextSample:
93  return OnTextSample(*stream_data->text_sample);
94  default:
95  return Status(error::INTERNAL_ERROR,
96  "Invalid stream data type for this handler");
97  }
98 }
99 
100 Status WebVttOutputHandler::OnFlushRequest(size_t input_stream_index) {
101  OnStreamEnd();
102  return Status::OK;
103 }
104 
105 WebVttSegmentedOutputHandler::WebVttSegmentedOutputHandler(
106  const MuxerOptions& muxer_options,
107  std::unique_ptr<MuxerListener> muxer_listener)
108  : muxer_options_(muxer_options),
109  muxer_listener_(std::move(muxer_listener)) {}
110 
111 Status WebVttSegmentedOutputHandler::OnStreamInfo(const StreamInfo& info) {
112  muxer_listener_->OnMediaStart(muxer_options_, info, info.time_scale(),
113  MuxerListener::kContainerText);
114  return Status::OK;
115 }
116 
117 Status WebVttSegmentedOutputHandler::OnSegmentInfo(const SegmentInfo& info) {
118  total_duration_ms_ += info.duration;
119 
120  const std::string& segment_template = muxer_options_.segment_template;
121  const uint32_t index = segment_index_++;
122  const uint64_t start = info.start_timestamp;
123  const uint64_t duration = info.duration;
124  const uint32_t bandwidth = 0;
125 
126  // Write all the samples to the file.
127  const std::string filename =
128  GetSegmentName(segment_template, start, index, bandwidth);
129 
130  // Write everything to the file before telling the manifest so that the
131  // file will exist on disk.
132  Status write_status = WriteSegmentToFile(filename);
133  if (!write_status.ok()) {
134  return write_status;
135  }
136 
137  // Update the manifest with our new file.
138  const uint64_t size = File::GetFileSize(filename.c_str());
139  muxer_listener_->OnNewSegment(filename, start, duration, size);
140 
141  return Status::OK;
142 }
143 
144 Status WebVttSegmentedOutputHandler::OnTextSample(const TextSample& sample) {
145  const std::string& id = sample.id();
146  const uint64_t start_ms = sample.start_time();
147  const uint64_t end_ms = sample.EndTime();
148  const std::string& settings = sample.settings();
149  const std::string& payload = sample.payload();
150 
151  WriteCue(id, start_ms, end_ms, settings, payload);
152  return Status::OK;
153 }
154 
155 Status WebVttSegmentedOutputHandler::OnStreamEnd() {
156  const float duration_ms = static_cast<float>(total_duration_ms_);
157  const float duration_seconds = duration_ms / 1000.0f;
158 
159  MuxerListener::MediaRanges empty_ranges;
160  muxer_listener_->OnMediaEnd(empty_ranges, duration_seconds);
161 
162  return Status::OK;
163 }
164 
165 } // namespace media
166 } // namespace shaka
STL namespace.
All the methods that are virtual are virtual for mocking.
static int64_t GetFileSize(const char *file_name)
Definition: file.cc:207
virtual bool Open()=0
Internal open. Should not be used directly.