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