Shaka Packager SDK
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 
14 namespace shaka {
15 namespace media {
16 namespace mp2t {
17 
18 namespace {
19 const int32_t kTsTimescale = 90000;
20 } // namespace
21 
22 TsMuxer::TsMuxer(const MuxerOptions& muxer_options) : Muxer(muxer_options) {}
23 TsMuxer::~TsMuxer() {}
24 
25 Status 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 
45 Status TsMuxer::Finalize() {
46  FireOnMediaEndEvent();
47  return segmenter_->Finalize();
48 }
49 
50 Status 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 
65 Status 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 
107 Status 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 
135 Status 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 
146 void TsMuxer::FireOnMediaStartEvent() {
147  if (!muxer_listener())
148  return;
149  muxer_listener()->OnMediaStart(options(), *streams().front(), kTsTimescale,
150  MuxerListener::kContainerMpeg2ts);
151 }
152 
153 void 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.
Definition: crypto_flags.cc:66