Shaka Packager SDK
Loading...
Searching...
No Matches
es_parser_h264.cc
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <packager/media/formats/mp2t/es_parser_h264.h>
6
7#include <cstdint>
8
9#include <absl/log/log.h>
10
11#include <packager/macros/logging.h>
12#include <packager/media/base/media_sample.h>
13#include <packager/media/base/timestamp.h>
14#include <packager/media/base/video_stream_info.h>
15#include <packager/media/codecs/avc_decoder_configuration_record.h>
16#include <packager/media/codecs/h264_byte_to_unit_stream_converter.h>
17#include <packager/media/codecs/h264_parser.h>
18#include <packager/media/formats/mp2t/mp2t_common.h>
19
20namespace shaka {
21namespace media {
22namespace mp2t {
23
24EsParserH264::EsParserH264(uint32_t pid,
25 const NewStreamInfoCB& new_stream_info_cb,
26 const EmitSampleCB& emit_sample_cb)
27 : EsParserH26x(Nalu::kH264,
28 std::unique_ptr<H26xByteToUnitStreamConverter>(
29 new H264ByteToUnitStreamConverter()),
30 pid,
31 emit_sample_cb),
32 new_stream_info_cb_(new_stream_info_cb),
33 decoder_config_check_pending_(false),
34 h264_parser_(new H264Parser()) {}
35
36EsParserH264::~EsParserH264() {}
37
38void EsParserH264::Reset() {
39 DVLOG(1) << "EsParserH264::Reset";
40 h264_parser_.reset(new H264Parser());
41 last_video_decoder_config_ = std::shared_ptr<StreamInfo>();
42 decoder_config_check_pending_ = false;
43 EsParserH26x::Reset();
44}
45
46bool EsParserH264::ProcessNalu(const Nalu& nalu,
47 VideoSliceInfo* video_slice_info) {
48 video_slice_info->valid = false;
49 switch (nalu.type()) {
50 case Nalu::H264_AUD: {
51 DVLOG(LOG_LEVEL_ES) << "Nalu: AUD";
52 break;
53 }
54 case Nalu::H264_SPS: {
55 DVLOG(LOG_LEVEL_ES) << "Nalu: SPS";
56 int sps_id;
57 auto status = h264_parser_->ParseSps(nalu, &sps_id);
58 if (status == H264Parser::kOk)
59 decoder_config_check_pending_ = true;
60 else if (status == H264Parser::kUnsupportedStream)
61 // Indicate the stream can't be parsed.
62 new_stream_info_cb_(nullptr);
63 else
64 return false;
65 break;
66 }
67 case Nalu::H264_PPS: {
68 DVLOG(LOG_LEVEL_ES) << "Nalu: PPS";
69 int pps_id;
70 auto status = h264_parser_->ParsePps(nalu, &pps_id);
71 if (status == H264Parser::kOk) {
72 decoder_config_check_pending_ = true;
73 } else if (status == H264Parser::kUnsupportedStream) {
74 // Indicate the stream can't be parsed.
75 new_stream_info_cb_(nullptr);
76 } else {
77 // Allow PPS parsing to fail if waiting for SPS.
78 if (last_video_decoder_config_)
79 return false;
80 }
81 break;
82 }
83 case Nalu::H264_IDRSlice:
84 case Nalu::H264_NonIDRSlice: {
85 const bool is_key_frame = (nalu.type() == Nalu::H264_IDRSlice);
86 DVLOG(LOG_LEVEL_ES) << "Nalu: slice IDR=" << is_key_frame;
87 H264SliceHeader shdr;
88 auto status = h264_parser_->ParseSliceHeader(nalu, &shdr);
89 if (status == H264Parser::kOk) {
90 video_slice_info->valid = true;
91 video_slice_info->is_key_frame = is_key_frame;
92 video_slice_info->frame_num = shdr.frame_num;
93 video_slice_info->pps_id = shdr.pic_parameter_set_id;
94 } else if (status == H264Parser::kUnsupportedStream) {
95 // Indicate the stream can't be parsed.
96 new_stream_info_cb_(nullptr);
97 } else {
98 // Only accept an invalid SPS/PPS at the beginning when the stream
99 // does not necessarily start with an SPS/PPS/IDR.
100 if (last_video_decoder_config_)
101 return false;
102 }
103 break;
104 }
105 default: {
106 DVLOG(LOG_LEVEL_ES) << "Nalu: " << nalu.type();
107 }
108 }
109
110 return true;
111}
112
113bool EsParserH264::UpdateVideoDecoderConfig(int pps_id) {
114 // Update the video decoder configuration if needed.
115 if (!decoder_config_check_pending_)
116 return true;
117
118 const H264Pps* pps = h264_parser_->GetPps(pps_id);
119 const H264Sps* sps;
120 if (!pps) {
121 // Only accept an invalid PPS at the beginning when the stream
122 // does not necessarily start with an SPS/PPS/IDR.
123 // In this case, the initial frames are conveyed to the upper layer with
124 // an invalid VideoDecoderConfig and it's up to the upper layer
125 // to process this kind of frame accordingly.
126 return last_video_decoder_config_ == nullptr;
127 } else {
128 sps = h264_parser_->GetSps(pps->seq_parameter_set_id);
129 if (!sps)
130 return false;
131 decoder_config_check_pending_ = false;
132 }
133
134 std::vector<uint8_t> decoder_config_record;
135 if (!stream_converter()->GetDecoderConfigurationRecord(
136 &decoder_config_record)) {
137 DLOG(ERROR) << "Failure to construct an AVCDecoderConfigurationRecord";
138 return false;
139 }
140
141 if (last_video_decoder_config_) {
142 if (last_video_decoder_config_->codec_config() != decoder_config_record) {
143 // Video configuration has changed. Issue warning.
144 // TODO(tinskip): Check the nature of the configuration change. Only
145 // minor configuration changes (such as frame ordering) can be handled
146 // gracefully by decoders without notification. Major changes (such as
147 // video resolution changes) should be treated as errors.
148 LOG(WARNING) << "H.264 decoder configuration has changed.";
149 last_video_decoder_config_->set_codec_config(decoder_config_record);
150 }
151 return true;
152 }
153
154 uint32_t coded_width = 0;
155 uint32_t coded_height = 0;
156 uint32_t pixel_width = 0;
157 uint32_t pixel_height = 0;
158 if (!ExtractResolutionFromSps(*sps, &coded_width, &coded_height, &pixel_width,
159 &pixel_height)) {
160 LOG(ERROR) << "Failed to parse SPS.";
161 return false;
162 }
163
164 const uint8_t nalu_length_size =
165 H26xByteToUnitStreamConverter::kUnitStreamNaluLengthSize;
166 const H26xStreamFormat stream_format = stream_converter()->stream_format();
167 const FourCC codec_fourcc =
168 stream_format == H26xStreamFormat::kNalUnitStreamWithParameterSetNalus
169 ? FOURCC_avc3
170 : FOURCC_avc1;
171 last_video_decoder_config_ = std::make_shared<VideoStreamInfo>(
172 pid(), kMpeg2Timescale, kInfiniteDuration, kCodecH264, stream_format,
173 AVCDecoderConfigurationRecord::GetCodecString(
174 codec_fourcc, decoder_config_record[1], decoder_config_record[2],
175 decoder_config_record[3]),
176 decoder_config_record.data(), decoder_config_record.size(), coded_width,
177 coded_height, pixel_width, pixel_height, sps->color_primaries,
178 sps->matrix_coefficients, sps->transfer_characteristics, 0,
179 nalu_length_size, std::string(), false);
180 DVLOG(1) << "Profile IDC: " << sps->profile_idc;
181 DVLOG(1) << "Level IDC: " << sps->level_idc;
182 DVLOG(1) << "log2_max_frame_num_minus4: " << sps->log2_max_frame_num_minus4;
183
184 // Video config notification.
185 new_stream_info_cb_(last_video_decoder_config_);
186
187 return true;
188}
189
190int64_t EsParserH264::CalculateSampleDuration(int pps_id) {
191 auto pps = h264_parser_->GetPps(pps_id);
192 if (pps) {
193 auto sps_id = pps->seq_parameter_set_id;
194 auto sps = h264_parser_->GetSps(sps_id);
195 if (sps && sps->timing_info_present_flag && sps->fixed_frame_rate_flag) {
196 return static_cast<int64_t>(kMpeg2Timescale) * sps->num_units_in_tick *
197 2 / sps->time_scale;
198 }
199 }
200 LOG(WARNING) << "[MPEG-2 TS] PID " << pid()
201 << " Cannot calculate frame rate from SPS.";
202 // Returns arbitrary safe duration
203 return 0.001 * kMpeg2Timescale; // 1ms.
204}
205
206} // namespace mp2t
207} // namespace media
208} // namespace shaka
All the methods that are virtual are virtual for mocking.