Shaka Packager SDK
Loading...
Searching...
No Matches
ts_muxer.cc
1// Copyright 2016 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/formats/mp2t/ts_muxer.h>
8
9#include <absl/log/check.h>
10
11#include <packager/macros/status.h>
12#include <packager/media/base/muxer_util.h>
13
14namespace shaka {
15namespace media {
16namespace mp2t {
17
18namespace {
19const int32_t kTsTimescale = 90000;
20} // namespace
21
22TsMuxer::TsMuxer(const MuxerOptions& muxer_options) : Muxer(muxer_options) {}
23TsMuxer::~TsMuxer() {}
24
25Status TsMuxer::InitializeMuxer() {
26 if (streams().size() > 1u)
27 return Status(error::MUXER_FAILURE, "Cannot handle more than one streams.");
28
29 if (options().segment_template.empty()) {
30 const std::string& file_name = options().output_file_name;
31 DCHECK(!file_name.empty());
32 output_file_.reset(File::Open(file_name.c_str(), "w"));
33 if (!output_file_) {
34 return Status(error::FILE_FAILURE,
35 "Cannot open file for write " + file_name);
36 }
37 }
38
39 segmenter_.reset(new TsSegmenter(options(), muxer_listener()));
40 Status status = segmenter_->Initialize(*streams()[0]);
41 FireOnMediaStartEvent();
42 return status;
43}
44
45Status TsMuxer::Finalize() {
46 FireOnMediaEndEvent();
47 return segmenter_->Finalize();
48}
49
50Status TsMuxer::AddMediaSample(size_t stream_id, const MediaSample& sample) {
51 DCHECK_EQ(stream_id, 0u);
52
53 // The duration of the first sample may have been adjusted, so use
54 // the duration of the second sample instead.
55 if (num_samples_ < 2) {
56 sample_durations_[num_samples_] =
57 sample.duration() * kTsTimescale / streams().front()->time_scale();
58 if (num_samples_ == 1 && muxer_listener())
59 muxer_listener()->OnSampleDurationReady(sample_durations_[num_samples_]);
60 num_samples_++;
61 }
62 return segmenter_->AddSample(sample);
63}
64
65Status TsMuxer::FinalizeSegment(size_t stream_id,
66 const SegmentInfo& segment_info) {
67 DCHECK_EQ(stream_id, 0u);
68
69 if (segment_info.is_subsegment)
70 return Status::OK;
71
72 Status s = segmenter_->FinalizeSegment(segment_info.start_timestamp,
73 segment_info.duration);
74 if (!s.ok())
75 return s;
76 if (!segmenter_->segment_started())
77 return Status::OK;
78
79 int64_t segment_start_timestamp = segmenter_->segment_start_timestamp();
80
81 std::string segment_path =
82 options().segment_template.empty()
83 ? options().output_file_name
84 : GetSegmentName(options().segment_template, segment_start_timestamp,
85 segment_info.segment_number, options().bandwidth);
86
87 const int64_t file_size = segmenter_->segment_buffer()->Size();
88
89 RETURN_IF_ERROR(WriteSegment(segment_path, segmenter_->segment_buffer()));
90
91 total_duration_ += segment_info.duration;
92
93 if (muxer_listener()) {
94 muxer_listener()->OnNewSegment(
95 segment_path,
96 segment_info.start_timestamp * segmenter_->timescale() +
97 segmenter_->transport_stream_timestamp_offset(),
98 segment_info.duration * segmenter_->timescale(), file_size,
99 segment_info.segment_number);
100 }
101
102 segmenter_->set_segment_started(false);
103
104 return Status::OK;
105}
106
107Status TsMuxer::WriteSegment(const std::string& segment_path,
108 BufferWriter* segment_buffer) {
109 std::unique_ptr<File, FileCloser> file;
110
111 if (output_file_) {
112 // This is in single segment mode.
113 Range range;
114 range.start = media_ranges_.subsegment_ranges.empty()
115 ? 0
116 : (media_ranges_.subsegment_ranges.back().end + 1);
117 range.end = range.start + segment_buffer->Size() - 1;
118 media_ranges_.subsegment_ranges.push_back(range);
119 } else {
120 file.reset(File::Open(segment_path.c_str(), "w"));
121 if (!file) {
122 return Status(error::FILE_FAILURE,
123 "Cannot open file for write " + segment_path);
124 }
125 }
126
127 RETURN_IF_ERROR(segment_buffer->WriteToFile(output_file_ ? output_file_.get()
128 : file.get()));
129
130 if (file)
131 RETURN_IF_ERROR(CloseFile(std::move(file)));
132 return Status::OK;
133}
134
135Status TsMuxer::CloseFile(std::unique_ptr<File, FileCloser> file) {
136 std::string file_name = file->file_name();
137 if (!file.release()->Close()) {
138 return Status(
139 error::FILE_FAILURE,
140 "Cannot close file " + file_name +
141 ", possibly file permission issue or running out of disk space.");
142 }
143 return Status::OK;
144}
145
146void TsMuxer::FireOnMediaStartEvent() {
147 if (!muxer_listener())
148 return;
149 muxer_listener()->OnMediaStart(options(), *streams().front(), kTsTimescale,
150 MuxerListener::kContainerMpeg2ts);
151}
152
153void TsMuxer::FireOnMediaEndEvent() {
154 if (!muxer_listener())
155 return;
156
157 muxer_listener()->OnMediaEnd(media_ranges_, total_duration_);
158}
159
160} // namespace mp2t
161} // namespace media
162} // namespace shaka
All the methods that are virtual are virtual for mocking.