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