7#include <packager/media/formats/mp2t/ts_segmenter.h>
15#include <absl/log/check.h>
16#include <absl/log/log.h>
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>
33const double kTsTimescale = 90000;
35bool IsAudioCodec(Codec codec) {
36 return codec >= kCodecAudio && codec < kCodecAudioMaxPlusOne;
39bool IsVideoCodec(Codec codec) {
40 return codec >= kCodecVideo && codec < kCodecVideoMaxPlusOne;
46 : listener_(listener),
47 transport_stream_timestamp_offset_(
48 options.transport_stream_timestamp_offset_ms * kTsTimescale / 1000),
49 pes_packet_generator_(
52TsSegmenter::~TsSegmenter() {}
55 if (!pes_packet_generator_->Initialize(stream_info)) {
56 return Status(error::MUXER_FAILURE,
57 "Failed to initialize PesPacketGenerator.");
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
65 return Status(error::MUXER_FAILURE,
"Unsupported stream type.");
68 codec_ = stream_info.codec();
69 if (stream_type == StreamType::kStreamAudio)
70 audio_codec_config_ = stream_info.codec_config();
72 timescale_scale_ = kTsTimescale / stream_info.time_scale();
82 std::unique_ptr<ProgramMapTableWriter> pmt_writer;
83 if (codec_ == kCodecAC3) {
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.");
95 const std::vector<uint8_t> setup_data(sample.data(),
96 sample.data() + kSetupDataSize);
98 }
else if (IsAudioCodec(codec_)) {
102 DCHECK(IsVideoCodec(codec_));
105 ts_writer_.reset(
new TsWriter(std::move(pmt_writer)));
108 if (sample.is_encrypted())
109 ts_writer_->SignalEncrypted();
111 if (!segment_started_ && !sample.is_key_frame())
112 LOG(WARNING) <<
"A segment will start with a non key frame.";
114 if (!pes_packet_generator_->PushSample(sample)) {
115 return Status(error::MUXER_FAILURE,
116 "Failed to add sample to PesPacketGenerator.");
118 return WritePesPackets();
122 ts_writer_ = std::move(writer);
126 std::unique_ptr<PesPacketGenerator> generator) {
127 pes_packet_generator_ = std::move(generator);
131 segment_started_ = value;
134Status TsSegmenter::StartSegmentIfNeeded(int64_t next_pts) {
135 if (segment_started_)
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;
144Status TsSegmenter::WritePesPackets() {
145 while (pes_packet_generator_->NumberOfReadyPesPackets() > 0u) {
146 std::unique_ptr<PesPacket> pes_packet =
147 pes_packet_generator_->GetNextPesPacket();
149 Status status = StartSegmentIfNeeded(pes_packet->pts());
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.");
159 uint64_t end_pos = segment_buffer_.Size();
161 listener_->
OnKeyFrame(timestamp, start_pos, end_pos - start_pos);
163 if (!ts_writer_->AddPesPacket(std::move(pes_packet), &segment_buffer_))
164 return Status(error::MUXER_FAILURE,
"Failed to add PES packet.");
171 if (!pes_packet_generator_->Flush()) {
172 return Status(error::MUXER_FAILURE,
"Failed to flush PesPacketGenerator.");
174 Status status = WritePesPackets();
All the methods that are virtual are virtual for mocking.