7 #include "packager/media/formats/mp2t/ts_segmenter.h"
11 #include "packager/media/base/audio_stream_info.h"
12 #include "packager/media/base/muxer_util.h"
13 #include "packager/media/base/video_stream_info.h"
14 #include "packager/media/event/muxer_listener.h"
15 #include "packager/media/formats/mp2t/pes_packet.h"
16 #include "packager/media/formats/mp2t/program_map_table_writer.h"
17 #include "packager/status.h"
18 #include "packager/status_macros.h"
25 const double kTsTimescale = 90000;
27 bool IsAudioCodec(Codec codec) {
28 return codec >= kCodecAudio && codec < kCodecAudioMaxPlusOne;
31 bool IsVideoCodec(Codec codec) {
32 return codec >= kCodecVideo && codec < kCodecVideoMaxPlusOne;
38 : muxer_options_(options),
40 transport_stream_timestamp_offset_(
41 options.transport_stream_timestamp_offset_ms * kTsTimescale / 1000),
42 pes_packet_generator_(
45 TsSegmenter::~TsSegmenter() {}
49 return Status(error::MUXER_FAILURE,
"Segment template not specified.");
50 if (!pes_packet_generator_->Initialize(stream_info)) {
51 return Status(error::MUXER_FAILURE,
52 "Failed to initialize PesPacketGenerator.");
55 const StreamType stream_type = stream_info.stream_type();
56 if (stream_type != StreamType::kStreamVideo &&
57 stream_type != StreamType::kStreamAudio) {
58 LOG(ERROR) <<
"TsWriter cannot handle stream type " << stream_type
60 return Status(error::MUXER_FAILURE,
"Unsupported stream type.");
63 codec_ = stream_info.codec();
64 if (stream_type == StreamType::kStreamAudio)
65 audio_codec_config_ = stream_info.codec_config();
67 timescale_scale_ = kTsTimescale / stream_info.time_scale();
77 std::unique_ptr<ProgramMapTableWriter> pmt_writer;
78 if (codec_ == kCodecAC3) {
85 const size_t kSetupDataSize = 10u;
86 if (sample.data_size() < kSetupDataSize) {
87 LOG(ERROR) <<
"Sample is too small for AC3: " << sample.data_size();
88 return Status(error::MUXER_FAILURE,
"Sample is too small for AC3.");
90 const std::vector<uint8_t> setup_data(sample.data(),
91 sample.data() + kSetupDataSize);
93 }
else if (IsAudioCodec(codec_)) {
97 DCHECK(IsVideoCodec(codec_));
100 ts_writer_.reset(
new TsWriter(std::move(pmt_writer)));
103 if (sample.is_encrypted())
104 ts_writer_->SignalEncrypted();
106 if (!segment_started_ && !sample.is_key_frame())
107 LOG(WARNING) <<
"A segment will start with a non key frame.";
109 if (!pes_packet_generator_->PushSample(sample)) {
110 return Status(error::MUXER_FAILURE,
111 "Failed to add sample to PesPacketGenerator.");
113 return WritePesPackets();
117 ts_writer_ = std::move(writer);
121 std::unique_ptr<PesPacketGenerator> generator) {
122 pes_packet_generator_ = std::move(generator);
126 segment_started_ = value;
129 Status TsSegmenter::StartSegmentIfNeeded(int64_t next_pts) {
130 if (segment_started_)
132 segment_start_timestamp_ = next_pts;
133 if (!ts_writer_->NewSegment(&segment_buffer_))
134 return Status(error::MUXER_FAILURE,
"Failed to initialize new segment.");
135 segment_started_ =
true;
139 Status TsSegmenter::WritePesPackets() {
140 while (pes_packet_generator_->NumberOfReadyPesPackets() > 0u) {
141 std::unique_ptr<PesPacket> pes_packet =
142 pes_packet_generator_->GetNextPesPacket();
144 Status status = StartSegmentIfNeeded(pes_packet->pts());
148 if (listener_ && IsVideoCodec(codec_) && pes_packet->is_key_frame()) {
150 uint64_t start_pos = segment_buffer_.Size();
151 const int64_t timestamp = pes_packet->pts();
152 if (!ts_writer_->AddPesPacket(std::move(pes_packet), &segment_buffer_))
153 return Status(error::MUXER_FAILURE,
"Failed to add PES packet.");
155 uint64_t end_pos = segment_buffer_.Size();
157 listener_->
OnKeyFrame(timestamp, start_pos, end_pos - start_pos);
159 if (!ts_writer_->AddPesPacket(std::move(pes_packet), &segment_buffer_))
160 return Status(error::MUXER_FAILURE,
"Failed to add PES packet.");
168 if (!pes_packet_generator_->Flush()) {
169 return Status(error::MUXER_FAILURE,
"Failed to flush PesPacketGenerator.");
171 Status status = WritePesPackets();
177 if (!segment_started_)
179 std::string segment_path =
181 segment_number_++, muxer_options_.
bandwidth);
183 const int64_t file_size = segment_buffer_.Size();
184 std::unique_ptr<File, FileCloser> segment_file;
185 segment_file.reset(
File::Open(segment_path.c_str(),
"w"));
187 return Status(error::FILE_FAILURE,
188 "Cannot open file for write " + segment_path);
191 RETURN_IF_ERROR(segment_buffer_.
WriteToFile(segment_file.get()));
193 if (!segment_file.release()->Close()) {
196 "Cannot close file " + segment_path +
197 ", possibly file permission issue or running out of disk space.");
202 start_timestamp * timescale_scale_ +
203 transport_stream_timestamp_offset_,
204 duration * timescale_scale_, file_size);
206 segment_started_ =
false;
virtual bool Open()=0
Internal open. Should not be used directly.
All the methods that are virtual are virtual for mocking.