2017-05-22 16:35:49 +00:00
|
|
|
// Copyright 2017 Google Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
|
|
|
|
#include "packager/media/formats/webvtt/webvtt_output_handler.h"
|
|
|
|
|
2018-05-21 16:58:02 +00:00
|
|
|
#include <algorithm> // needed for min and max
|
|
|
|
|
2017-05-22 16:35:49 +00:00
|
|
|
#include "packager/base/logging.h"
|
|
|
|
#include "packager/file/file.h"
|
2018-05-21 16:58:02 +00:00
|
|
|
#include "packager/file/file_closer.h"
|
2017-05-22 16:35:49 +00:00
|
|
|
#include "packager/media/base/muxer_util.h"
|
2018-05-21 16:58:02 +00:00
|
|
|
#include "packager/status_macros.h"
|
2017-05-22 16:35:49 +00:00
|
|
|
|
|
|
|
namespace shaka {
|
|
|
|
namespace media {
|
2018-04-06 20:19:39 +00:00
|
|
|
namespace {
|
|
|
|
double kMillisecondsToSeconds = 1000.0;
|
|
|
|
} // namespace
|
|
|
|
|
2018-05-21 16:58:02 +00:00
|
|
|
WebVttTextOutputHandler::WebVttTextOutputHandler(
|
|
|
|
const MuxerOptions& muxer_options,
|
|
|
|
std::unique_ptr<MuxerListener> muxer_listener)
|
|
|
|
: muxer_options_(muxer_options),
|
|
|
|
muxer_listener_(std::move(muxer_listener)) {}
|
2017-05-22 16:35:49 +00:00
|
|
|
|
2018-05-21 16:58:02 +00:00
|
|
|
Status WebVttTextOutputHandler::InitializeInternal() {
|
2017-05-22 16:35:49 +00:00
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:58:02 +00:00
|
|
|
Status WebVttTextOutputHandler::Process(
|
|
|
|
std::unique_ptr<StreamData> stream_data) {
|
2017-05-22 16:35:49 +00:00
|
|
|
switch (stream_data->stream_data_type) {
|
|
|
|
case StreamDataType::kStreamInfo:
|
|
|
|
return OnStreamInfo(*stream_data->stream_info);
|
|
|
|
case StreamDataType::kSegmentInfo:
|
|
|
|
return OnSegmentInfo(*stream_data->segment_info);
|
2018-04-06 20:19:39 +00:00
|
|
|
case StreamDataType::kCueEvent:
|
|
|
|
return OnCueEvent(*stream_data->cue_event);
|
2017-05-22 16:35:49 +00:00
|
|
|
case StreamDataType::kTextSample:
|
2018-05-21 16:58:02 +00:00
|
|
|
OnTextSample(*stream_data->text_sample);
|
|
|
|
return Status::OK;
|
2017-05-22 16:35:49 +00:00
|
|
|
default:
|
|
|
|
return Status(error::INTERNAL_ERROR,
|
|
|
|
"Invalid stream data type for this handler");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:58:02 +00:00
|
|
|
Status WebVttTextOutputHandler::OnFlushRequest(size_t input_stream_index) {
|
|
|
|
DCHECK_EQ(buffer_.sample_count(), 0u)
|
|
|
|
<< "There should have been a segment info before flushing that would "
|
|
|
|
"have cleared out all the samples.";
|
|
|
|
|
|
|
|
const float duration_ms = static_cast<float>(total_duration_ms_);
|
|
|
|
const float duration_seconds = duration_ms / 1000.0f;
|
|
|
|
|
|
|
|
MuxerListener::MediaRanges empty_ranges;
|
|
|
|
muxer_listener_->OnMediaEnd(empty_ranges, duration_seconds);
|
|
|
|
|
2017-05-22 16:35:49 +00:00
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:58:02 +00:00
|
|
|
Status WebVttTextOutputHandler::OnStreamInfo(const StreamInfo& info) {
|
2017-05-22 16:35:49 +00:00
|
|
|
muxer_listener_->OnMediaStart(muxer_options_, info, info.time_scale(),
|
|
|
|
MuxerListener::kContainerText);
|
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:58:02 +00:00
|
|
|
Status WebVttTextOutputHandler::OnSegmentInfo(const SegmentInfo& info) {
|
2017-05-22 16:35:49 +00:00
|
|
|
total_duration_ms_ += info.duration;
|
|
|
|
|
|
|
|
const std::string& segment_template = muxer_options_.segment_template;
|
|
|
|
const uint32_t index = segment_index_++;
|
|
|
|
const uint64_t start = info.start_timestamp;
|
|
|
|
const uint64_t duration = info.duration;
|
2018-05-21 16:58:02 +00:00
|
|
|
const uint32_t bandwidth = muxer_options_.bandwidth;
|
2017-05-22 16:35:49 +00:00
|
|
|
|
|
|
|
const std::string filename =
|
|
|
|
GetSegmentName(segment_template, start, index, bandwidth);
|
|
|
|
|
|
|
|
// Write everything to the file before telling the manifest so that the
|
|
|
|
// file will exist on disk.
|
2018-05-21 16:58:02 +00:00
|
|
|
std::unique_ptr<File, FileCloser> file(File::Open(filename.c_str(), "w"));
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
return Status(error::FILE_FAILURE, "Failed to open " + filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer_.WriteTo(file.get());
|
|
|
|
buffer_.Reset();
|
|
|
|
|
|
|
|
if (!file.release()->Close()) {
|
|
|
|
return Status(error::FILE_FAILURE, "Failed to close " + filename);
|
2017-05-22 16:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update the manifest with our new file.
|
|
|
|
const uint64_t size = File::GetFileSize(filename.c_str());
|
|
|
|
muxer_listener_->OnNewSegment(filename, start, duration, size);
|
|
|
|
|
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:58:02 +00:00
|
|
|
Status WebVttTextOutputHandler::OnCueEvent(const CueEvent& event) {
|
2018-04-06 20:19:39 +00:00
|
|
|
double timestamp_seconds = event.time_in_seconds;
|
|
|
|
double timestamp_ms = timestamp_seconds * kMillisecondsToSeconds;
|
|
|
|
uint64_t timestamp = static_cast<uint64_t>(timestamp_ms);
|
|
|
|
muxer_listener_->OnCueEvent(timestamp, event.cue_data);
|
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:58:02 +00:00
|
|
|
void WebVttTextOutputHandler::OnTextSample(const TextSample& sample) {
|
|
|
|
// Skip empty samples. It is normal to see empty samples as earlier in the
|
|
|
|
// pipeline we pad the stream to remove gaps.
|
|
|
|
if (sample.payload().size()) {
|
|
|
|
buffer_.Append(sample);
|
2018-04-02 23:20:14 +00:00
|
|
|
}
|
2017-05-22 16:35:49 +00:00
|
|
|
}
|
|
|
|
} // namespace media
|
|
|
|
} // namespace shaka
|