5 #include <packager/media/formats/mp2t/es_parser_h26x.h>
9 #include <absl/log/check.h>
10 #include <absl/log/log.h>
12 #include <packager/macros/logging.h>
13 #include <packager/media/base/media_sample.h>
14 #include <packager/media/base/offset_byte_queue.h>
15 #include <packager/media/base/timestamp.h>
16 #include <packager/media/base/video_stream_info.h>
17 #include <packager/media/codecs/h26x_byte_to_unit_stream_converter.h>
18 #include <packager/media/formats/mp2t/mp2t_common.h>
26 const int kStartCodeSize = 3;
27 const int kH264NaluHeaderSize = 1;
28 const int kH265NaluHeaderSize = 2;
32 EsParserH26x::EsParserH26x(
34 std::unique_ptr<H26xByteToUnitStreamConverter> stream_converter,
36 const EmitSampleCB& emit_sample_cb)
38 emit_sample_cb_(emit_sample_cb),
40 es_queue_(new media::OffsetByteQueue()),
41 stream_converter_(std::move(stream_converter)) {}
43 EsParserH26x::~EsParserH26x() {}
45 bool EsParserH26x::Parse(
const uint8_t* buf,
58 if (pts == kNoTimestamp) {
59 DVLOG(1) <<
"Each video PES should have a PTS";
61 TimingDesc timing_desc;
62 timing_desc.pts = pts;
63 timing_desc.dts = (dts != kNoTimestamp) ? dts : pts;
66 timing_desc_list_.push_back(
67 std::pair<int64_t, TimingDesc>(es_queue_->tail(), timing_desc));
71 const size_t kWarningSize =
73 LOG_IF(WARNING, timing_desc_list_.size() >= kWarningSize)
74 <<
"Unusually large number of cached timestamps ("
75 << timing_desc_list_.size() <<
").";
79 es_queue_->Push(buf, size);
80 return ParseInternal();
83 bool EsParserH26x::Flush() {
84 DVLOG(1) <<
"EsParserH26x::Flush";
91 if (type_ == Nalu::kH264) {
92 const uint8_t aud[] = {0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x01, 0x09};
93 es_queue_->Push(aud,
sizeof(aud));
95 DCHECK_EQ(Nalu::kH265, type_);
96 const uint8_t aud[] = {0x00, 0x00, 0x01, 0x46, 0x01,
97 0x00, 0x00, 0x01, 0x46, 0x01};
98 es_queue_->Push(aud,
sizeof(aud));
101 RCHECK(ParseInternal());
103 if (pending_sample_) {
105 if (!pending_sample_duration_) {
106 pending_sample_duration_ = CalculateSampleDuration(pending_sample_pps_id_);
108 pending_sample_->set_duration(pending_sample_duration_);
109 emit_sample_cb_(std::move(pending_sample_));
114 void EsParserH26x::Reset() {
115 es_queue_.reset(
new media::OffsetByteQueue());
116 current_search_position_ = 0;
117 current_access_unit_position_ = 0;
118 current_video_slice_info_.valid =
false;
119 next_access_unit_position_set_ =
false;
120 next_access_unit_position_ = 0;
121 current_nalu_info_.reset();
122 timing_desc_list_.clear();
123 pending_sample_ = std::shared_ptr<MediaSample>();
124 pending_sample_duration_ = 0;
125 waiting_for_key_frame_ =
true;
128 bool EsParserH26x::SearchForNalu(uint64_t* position, Nalu* nalu) {
131 es_queue_->PeekAt(current_search_position_, &es, &es_size);
134 uint64_t start_code_offset;
135 uint8_t start_code_size;
136 const bool start_code_found = NaluReader::FindStartCode(
137 es, es_size, &start_code_offset, &start_code_size);
139 if (!start_code_found) {
141 if (es_size > kStartCodeSize)
142 current_search_position_ += es_size - kStartCodeSize;
147 const uint8_t* next_nalu_ptr = es + start_code_offset + start_code_size;
149 const int64_t next_nalu_size = es_size - start_code_offset - start_code_size;
151 (type_ == Nalu::kH264 ? kH264NaluHeaderSize : kH265NaluHeaderSize)) {
157 current_search_position_ += start_code_offset + start_code_size;
161 if (!next_nalu_info_)
162 next_nalu_info_.reset(
new NaluInfo);
163 if (!next_nalu_info_->nalu.Initialize(type_, next_nalu_ptr, next_nalu_size)) {
165 return SearchForNalu(position, nalu);
167 next_nalu_info_->position = current_search_position_ - start_code_size;
168 next_nalu_info_->start_code_size = start_code_size;
170 const bool current_nalu_set = current_nalu_info_ ? true :
false;
171 if (current_nalu_info_) {
173 *position = current_nalu_info_->position;
175 const uint8_t* current_nalu_ptr =
177 (current_nalu_info_->position + current_nalu_info_->start_code_size) -
178 current_search_position_;
179 const uint64_t current_nalu_size = next_nalu_info_->position -
180 current_nalu_info_->position -
181 current_nalu_info_->start_code_size;
182 CHECK(nalu->Initialize(type_, current_nalu_ptr, current_nalu_size));
184 current_nalu_info_.swap(next_nalu_info_);
185 return current_nalu_set ? true : SearchForNalu(position, nalu);
188 bool EsParserH26x::ParseInternal() {
191 VideoSliceInfo video_slice_info;
192 while (SearchForNalu(&position, &nalu)) {
202 if (nalu.can_start_access_unit()) {
203 if (!next_access_unit_position_set_) {
204 next_access_unit_position_set_ =
true;
205 next_access_unit_position_ = position;
207 RCHECK(ProcessNalu(nalu, &video_slice_info));
208 if (nalu.is_vcl() && !video_slice_info.valid) {
211 DCHECK(!current_video_slice_info_.valid);
212 next_access_unit_position_set_ =
false;
215 }
else if (nalu.is_vcl()) {
218 next_access_unit_position_set_ =
false;
226 RCHECK(EmitCurrentAccessUnit());
232 if (!video_slice_info.valid)
237 bool is_first_vcl_nalu =
true;
238 if (type_ == Nalu::kH264) {
239 if (current_video_slice_info_.valid) {
243 video_slice_info.frame_num != current_video_slice_info_.frame_num ||
244 video_slice_info.pps_id != current_video_slice_info_.pps_id;
247 if (!is_first_vcl_nalu) {
250 next_access_unit_position_set_ =
false;
254 DCHECK(next_access_unit_position_set_);
255 RCHECK(EmitCurrentAccessUnit());
258 es_queue_->Trim(next_access_unit_position_);
260 current_access_unit_position_ = next_access_unit_position_;
261 current_video_slice_info_ = video_slice_info;
262 next_access_unit_position_set_ =
false;
267 bool EsParserH26x::EmitCurrentAccessUnit() {
268 if (current_video_slice_info_.valid) {
269 if (current_video_slice_info_.is_key_frame)
270 waiting_for_key_frame_ =
false;
271 if (!waiting_for_key_frame_) {
273 EmitFrame(current_access_unit_position_,
274 next_access_unit_position_ - current_access_unit_position_,
275 current_video_slice_info_.is_key_frame,
276 current_video_slice_info_.pps_id));
278 current_video_slice_info_.valid =
false;
283 bool EsParserH26x::EmitFrame(int64_t access_unit_pos,
284 int access_unit_size,
288 TimingDesc current_timing_desc = {kNoTimestamp, kNoTimestamp};
289 while (!timing_desc_list_.empty() &&
290 timing_desc_list_.front().first <= access_unit_pos) {
291 current_timing_desc = timing_desc_list_.front().second;
292 timing_desc_list_.pop_front();
294 if (current_timing_desc.pts == kNoTimestamp)
298 DVLOG(LOG_LEVEL_ES) <<
"Emit frame: stream_pos=" << access_unit_pos
299 <<
" size=" << access_unit_size <<
" pts "
300 << current_timing_desc.pts <<
" timing_desc_list size "
301 << timing_desc_list_.size();
304 es_queue_->PeekAt(access_unit_pos, &es, &es_size);
307 std::vector<uint8_t> converted_frame;
308 if (!stream_converter_->ConvertByteStreamToNalUnitStream(
309 es, access_unit_size, &converted_frame)) {
310 DLOG(ERROR) <<
"Failure to convert video frame to unit stream format.";
315 RCHECK(UpdateVideoDecoderConfig(pps_id));
319 std::shared_ptr<MediaSample> media_sample = MediaSample::CopyFrom(
320 converted_frame.data(), converted_frame.size(), is_key_frame);
321 media_sample->set_dts(current_timing_desc.dts);
322 media_sample->set_pts(current_timing_desc.pts);
323 if (pending_sample_) {
324 if (media_sample->dts() <= pending_sample_->dts()) {
325 LOG(WARNING) <<
"[MPEG-2 TS] PID " << pid() <<
" dts "
326 << media_sample->dts()
327 <<
" less than or equal to previous dts "
328 << pending_sample_->dts();
331 const int64_t kArbitrarySmallDuration = 0.001 * kMpeg2Timescale;
332 pending_sample_->set_duration(kArbitrarySmallDuration);
334 int64_t sample_duration = media_sample->dts() - pending_sample_->dts();
335 pending_sample_->set_duration(sample_duration);
337 const int kArbitraryGapScale = 10;
338 if (pending_sample_duration_ &&
339 sample_duration > kArbitraryGapScale * pending_sample_duration_) {
340 LOG(WARNING) <<
"[MPEG-2 TS] PID " << pid() <<
" Possible GAP at dts "
341 << pending_sample_->dts() <<
" with next sample at dts "
342 << media_sample->dts() <<
" (difference "
343 << sample_duration <<
")";
346 pending_sample_duration_ = sample_duration;
348 emit_sample_cb_(std::move(pending_sample_));
350 pending_sample_ = media_sample;
351 pending_sample_pps_id_ = pps_id;
All the methods that are virtual are virtual for mocking.