Shaka Packager SDK
Loading...
Searching...
No Matches
ts_segmenter.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_segmenter.h>
8
9#include <memory>
10
11#include <absl/log/check.h>
12
13#include <packager/macros/status.h>
14#include <packager/media/base/audio_stream_info.h>
15#include <packager/media/base/muxer_util.h>
16#include <packager/media/base/video_stream_info.h>
17#include <packager/media/event/muxer_listener.h>
18#include <packager/media/formats/mp2t/pes_packet.h>
19#include <packager/media/formats/mp2t/program_map_table_writer.h>
20#include <packager/status.h>
21
22namespace shaka {
23namespace media {
24namespace mp2t {
25
26namespace {
27const double kTsTimescale = 90000;
28
29bool IsAudioCodec(Codec codec) {
30 return codec >= kCodecAudio && codec < kCodecAudioMaxPlusOne;
31}
32
33bool IsVideoCodec(Codec codec) {
34 return codec >= kCodecVideo && codec < kCodecVideoMaxPlusOne;
35}
36
37} // namespace
38
40 : listener_(listener),
41 transport_stream_timestamp_offset_(
42 options.transport_stream_timestamp_offset_ms * kTsTimescale / 1000),
43 pes_packet_generator_(
44 new PesPacketGenerator(transport_stream_timestamp_offset_)) {}
45
46TsSegmenter::~TsSegmenter() {}
47
48Status TsSegmenter::Initialize(const StreamInfo& stream_info) {
49 if (!pes_packet_generator_->Initialize(stream_info)) {
50 return Status(error::MUXER_FAILURE,
51 "Failed to initialize PesPacketGenerator.");
52 }
53
54 const StreamType stream_type = stream_info.stream_type();
55 if (stream_type != StreamType::kStreamVideo &&
56 stream_type != StreamType::kStreamAudio) {
57 LOG(ERROR) << "TsWriter cannot handle stream type " << stream_type
58 << " yet.";
59 return Status(error::MUXER_FAILURE, "Unsupported stream type.");
60 }
61
62 codec_ = stream_info.codec();
63 if (stream_type == StreamType::kStreamAudio)
64 audio_codec_config_ = stream_info.codec_config();
65
66 timescale_scale_ = kTsTimescale / stream_info.time_scale();
67 return Status::OK;
68}
69
71 return Status::OK;
72}
73
74Status TsSegmenter::AddSample(const MediaSample& sample) {
75 if (!ts_writer_) {
76 std::unique_ptr<ProgramMapTableWriter> pmt_writer;
77 if (codec_ == kCodecAC3) {
78 // https://goo.gl/N7Tvqi MPEG-2 Stream Encryption Format for HTTP Live
79 // Streaming 2.3.2.2 AC-3 Setup: For AC-3, the setup_data in the
80 // audio_setup_information is the first 10 bytes of the audio data (the
81 // syncframe()).
82 // For unencrypted AC3, the setup_data is not used, so what is in there
83 // does not matter.
84 const size_t kSetupDataSize = 10u;
85 if (sample.data_size() < kSetupDataSize) {
86 LOG(ERROR) << "Sample is too small for AC3: " << sample.data_size();
87 return Status(error::MUXER_FAILURE, "Sample is too small for AC3.");
88 }
89 const std::vector<uint8_t> setup_data(sample.data(),
90 sample.data() + kSetupDataSize);
91 pmt_writer.reset(new AudioProgramMapTableWriter(codec_, setup_data));
92 } else if (IsAudioCodec(codec_)) {
93 pmt_writer.reset(
94 new AudioProgramMapTableWriter(codec_, audio_codec_config_));
95 } else {
96 DCHECK(IsVideoCodec(codec_));
97 pmt_writer.reset(new VideoProgramMapTableWriter(codec_));
98 }
99 ts_writer_.reset(new TsWriter(std::move(pmt_writer)));
100 }
101
102 if (sample.is_encrypted())
103 ts_writer_->SignalEncrypted();
104
105 if (!segment_started_ && !sample.is_key_frame())
106 LOG(WARNING) << "A segment will start with a non key frame.";
107
108 if (!pes_packet_generator_->PushSample(sample)) {
109 return Status(error::MUXER_FAILURE,
110 "Failed to add sample to PesPacketGenerator.");
111 }
112 return WritePesPackets();
113}
114
115void TsSegmenter::InjectTsWriterForTesting(std::unique_ptr<TsWriter> writer) {
116 ts_writer_ = std::move(writer);
117}
118
120 std::unique_ptr<PesPacketGenerator> generator) {
121 pes_packet_generator_ = std::move(generator);
122}
123
125 segment_started_ = value;
126}
127
128Status TsSegmenter::StartSegmentIfNeeded(int64_t next_pts) {
129 if (segment_started_)
130 return Status::OK;
131 segment_start_timestamp_ = next_pts;
132 if (!ts_writer_->NewSegment(&segment_buffer_))
133 return Status(error::MUXER_FAILURE, "Failed to initialize new segment.");
134 segment_started_ = true;
135 return Status::OK;
136}
137
138Status TsSegmenter::WritePesPackets() {
139 while (pes_packet_generator_->NumberOfReadyPesPackets() > 0u) {
140 std::unique_ptr<PesPacket> pes_packet =
141 pes_packet_generator_->GetNextPesPacket();
142
143 Status status = StartSegmentIfNeeded(pes_packet->pts());
144 if (!status.ok())
145 return status;
146
147 if (listener_ && IsVideoCodec(codec_) && pes_packet->is_key_frame()) {
148
149 uint64_t start_pos = segment_buffer_.Size();
150 const int64_t timestamp = pes_packet->pts();
151 if (!ts_writer_->AddPesPacket(std::move(pes_packet), &segment_buffer_))
152 return Status(error::MUXER_FAILURE, "Failed to add PES packet.");
153
154 uint64_t end_pos = segment_buffer_.Size();
155
156 listener_->OnKeyFrame(timestamp, start_pos, end_pos - start_pos);
157 } else {
158 if (!ts_writer_->AddPesPacket(std::move(pes_packet), &segment_buffer_))
159 return Status(error::MUXER_FAILURE, "Failed to add PES packet.");
160 }
161 }
162 return Status::OK;
163}
164
165Status TsSegmenter::FinalizeSegment(int64_t start_timestamp, int64_t duration) {
166 if (!pes_packet_generator_->Flush()) {
167 return Status(error::MUXER_FAILURE, "Failed to flush PesPacketGenerator.");
168 }
169 Status status = WritePesPackets();
170 if (!status.ok())
171 return status;
172 return Status::OK;
173}
174
175} // namespace mp2t
176} // namespace media
177} // namespace shaka
Class to hold a media sample.
virtual void OnKeyFrame(int64_t timestamp, uint64_t start_byte_offset, uint64_t size)=0
Abstract class holds stream information.
Definition stream_info.h:71
ProgramMapTableWriter for video codecs.
Status FinalizeSegment(int64_t start_timestamp, int64_t duration)
void SetSegmentStartedForTesting(bool value)
Only for testing.
Status Initialize(const StreamInfo &stream_info)
void InjectPesPacketGeneratorForTesting(std::unique_ptr< PesPacketGenerator > generator)
Only for testing.
Status AddSample(const MediaSample &sample)
void InjectTsWriterForTesting(std::unique_ptr< TsWriter > writer)
Only for testing.
TsSegmenter(const MuxerOptions &options, MuxerListener *listener)
ProgramMapTableWriter for video codecs.
All the methods that are virtual are virtual for mocking.
This structure contains the list of configuration options for Muxer.