blob: 2b247c9061ed3766282881f8fea64da903e1832a [file] [log] [blame]
philipel34852cf2016-11-03 04:03:01 -07001/*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/video_coding/h264_sps_pps_tracker.h"
philipel34852cf2016-11-03 04:03:01 -070012
13#include <string>
philipel022b54e2016-12-20 04:15:59 -080014#include <utility>
philipel34852cf2016-11-03 04:03:01 -070015
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "common_video/h264/h264_common.h"
17#include "common_video/h264/pps_parser.h"
18#include "common_video/h264/sps_parser.h"
Rasmus Brandt88f080a2017-11-02 14:28:06 +010019#include "modules/video_coding/codecs/h264/include/h264_globals.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "modules/video_coding/frame_object.h"
21#include "modules/video_coding/packet_buffer.h"
22#include "rtc_base/checks.h"
23#include "rtc_base/logging.h"
philipel34852cf2016-11-03 04:03:01 -070024
25namespace webrtc {
26namespace video_coding {
27
28namespace {
29const uint8_t start_code_h264[] = {0, 0, 0, 1};
30} // namespace
31
philipela75d12d2016-11-07 05:11:28 -080032H264SpsPpsTracker::PacketAction H264SpsPpsTracker::CopyAndFixBitstream(
33 VCMPacket* packet) {
philipel34852cf2016-11-03 04:03:01 -070034 RTC_DCHECK(packet->codec == kVideoCodecH264);
35
36 const uint8_t* data = packet->dataPtr;
37 const size_t data_size = packet->sizeBytes;
38 const RTPVideoHeader& video_header = packet->video_header;
Rasmus Brandt88f080a2017-11-02 14:28:06 +010039 RTPVideoHeaderH264* codec_header = &packet->video_header.codecHeader.H264;
philipel34852cf2016-11-03 04:03:01 -070040
philipel83c97da2017-06-21 07:22:40 -070041 bool append_sps_pps = false;
42 auto sps = sps_data_.end();
43 auto pps = pps_data_.end();
44
Rasmus Brandt88f080a2017-11-02 14:28:06 +010045 for (size_t i = 0; i < codec_header->nalus_length; ++i) {
46 const NaluInfo& nalu = codec_header->nalus[i];
philipel34852cf2016-11-03 04:03:01 -070047 switch (nalu.type) {
48 case H264::NaluType::kSps: {
philipel6585f702017-03-17 06:12:33 -070049 sps_data_[nalu.sps_id].width = packet->width;
50 sps_data_[nalu.sps_id].height = packet->height;
philipel34852cf2016-11-03 04:03:01 -070051 break;
52 }
53 case H264::NaluType::kPps: {
philipel34852cf2016-11-03 04:03:01 -070054 pps_data_[nalu.pps_id].sps_id = nalu.sps_id;
philipel34852cf2016-11-03 04:03:01 -070055 break;
56 }
57 case H264::NaluType::kIdr: {
58 // If this is the first packet of an IDR, make sure we have the required
59 // SPS/PPS and also calculate how much extra space we need in the buffer
60 // to prepend the SPS/PPS to the bitstream with start codes.
johan0d1b2b62017-01-10 04:21:35 -080061 if (video_header.is_first_packet_in_frame) {
philipel34852cf2016-11-03 04:03:01 -070062 if (nalu.pps_id == -1) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010063 RTC_LOG(LS_WARNING) << "No PPS id in IDR nalu.";
philipela75d12d2016-11-07 05:11:28 -080064 return kRequestKeyframe;
philipel34852cf2016-11-03 04:03:01 -070065 }
66
philipel83c97da2017-06-21 07:22:40 -070067 pps = pps_data_.find(nalu.pps_id);
philipel34852cf2016-11-03 04:03:01 -070068 if (pps == pps_data_.end()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010069 RTC_LOG(LS_WARNING)
70 << "No PPS with id << " << nalu.pps_id << " received";
philipela75d12d2016-11-07 05:11:28 -080071 return kRequestKeyframe;
philipel34852cf2016-11-03 04:03:01 -070072 }
73
philipel83c97da2017-06-21 07:22:40 -070074 sps = sps_data_.find(pps->second.sps_id);
philipel34852cf2016-11-03 04:03:01 -070075 if (sps == sps_data_.end()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010076 RTC_LOG(LS_WARNING)
philipel83c97da2017-06-21 07:22:40 -070077 << "No SPS with id << " << pps->second.sps_id << " received";
philipela75d12d2016-11-07 05:11:28 -080078 return kRequestKeyframe;
philipel34852cf2016-11-03 04:03:01 -070079 }
80
philipel83c97da2017-06-21 07:22:40 -070081 // Since the first packet of every keyframe should have its width and
82 // height set we set it here in the case of it being supplied out of
83 // band.
84 packet->width = sps->second.width;
85 packet->height = sps->second.height;
86
87 // If the SPS/PPS was supplied out of band then we will have saved
88 // the actual bitstream in |data|.
89 if (sps->second.data && pps->second.data) {
90 RTC_DCHECK_GT(sps->second.size, 0);
91 RTC_DCHECK_GT(pps->second.size, 0);
92 append_sps_pps = true;
93 }
philipel34852cf2016-11-03 04:03:01 -070094 }
philipel83c97da2017-06-21 07:22:40 -070095 break;
philipel34852cf2016-11-03 04:03:01 -070096 }
philipel83c97da2017-06-21 07:22:40 -070097 default:
98 break;
philipel34852cf2016-11-03 04:03:01 -070099 }
100 }
101
philipel83c97da2017-06-21 07:22:40 -0700102 RTC_CHECK(!append_sps_pps ||
103 (sps != sps_data_.end() && pps != pps_data_.end()));
philipel34852cf2016-11-03 04:03:01 -0700104
105 // Calculate how much space we need for the rest of the bitstream.
philipel83c97da2017-06-21 07:22:40 -0700106 size_t required_size = 0;
107
108 if (append_sps_pps) {
109 required_size += sps->second.size + sizeof(start_code_h264);
110 required_size += pps->second.size + sizeof(start_code_h264);
111 }
112
Rasmus Brandt88f080a2017-11-02 14:28:06 +0100113 if (codec_header->packetization_type == kH264StapA) {
philipel34852cf2016-11-03 04:03:01 -0700114 const uint8_t* nalu_ptr = data + 1;
115 while (nalu_ptr < data + data_size) {
johan0d1b2b62017-01-10 04:21:35 -0800116 RTC_DCHECK(video_header.is_first_packet_in_frame);
philipel34852cf2016-11-03 04:03:01 -0700117 required_size += sizeof(start_code_h264);
118
119 // The first two bytes describe the length of a segment.
120 uint16_t segment_length = nalu_ptr[0] << 8 | nalu_ptr[1];
121 nalu_ptr += 2;
122
123 required_size += segment_length;
124 nalu_ptr += segment_length;
125 }
126 } else {
johan0d1b2b62017-01-10 04:21:35 -0800127 if (video_header.is_first_packet_in_frame)
philipel34852cf2016-11-03 04:03:01 -0700128 required_size += sizeof(start_code_h264);
129 required_size += data_size;
130 }
131
132 // Then we copy to the new buffer.
133 uint8_t* buffer = new uint8_t[required_size];
134 uint8_t* insert_at = buffer;
135
philipel83c97da2017-06-21 07:22:40 -0700136 if (append_sps_pps) {
philipel34852cf2016-11-03 04:03:01 -0700137 // Insert SPS.
138 memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
139 insert_at += sizeof(start_code_h264);
philipel83c97da2017-06-21 07:22:40 -0700140 memcpy(insert_at, sps->second.data.get(), sps->second.size);
141 insert_at += sps->second.size;
philipel34852cf2016-11-03 04:03:01 -0700142
143 // Insert PPS.
144 memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
145 insert_at += sizeof(start_code_h264);
philipel83c97da2017-06-21 07:22:40 -0700146 memcpy(insert_at, pps->second.data.get(), pps->second.size);
147 insert_at += pps->second.size;
Rasmus Brandt88f080a2017-11-02 14:28:06 +0100148
149 // Update codec header to reflect the newly added SPS and PPS.
150 NaluInfo sps_info;
151 sps_info.type = H264::NaluType::kSps;
152 sps_info.sps_id = sps->first;
153 sps_info.pps_id = -1;
154 NaluInfo pps_info;
155 pps_info.type = H264::NaluType::kPps;
156 pps_info.sps_id = sps->first;
157 pps_info.pps_id = pps->first;
158 if (codec_header->nalus_length + 2 <= kMaxNalusPerPacket) {
159 codec_header->nalus[codec_header->nalus_length++] = sps_info;
160 codec_header->nalus[codec_header->nalus_length++] = pps_info;
161 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100162 RTC_LOG(LS_WARNING) << "Not enough space in H.264 codec header to insert "
163 "SPS/PPS provided out-of-band.";
Rasmus Brandt88f080a2017-11-02 14:28:06 +0100164 }
philipel34852cf2016-11-03 04:03:01 -0700165 }
166
167 // Copy the rest of the bitstream and insert start codes.
Rasmus Brandt88f080a2017-11-02 14:28:06 +0100168 if (codec_header->packetization_type == kH264StapA) {
philipel34852cf2016-11-03 04:03:01 -0700169 const uint8_t* nalu_ptr = data + 1;
170 while (nalu_ptr < data + data_size) {
171 memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
172 insert_at += sizeof(start_code_h264);
173
174 // The first two bytes describe the length of a segment.
175 uint16_t segment_length = nalu_ptr[0] << 8 | nalu_ptr[1];
176 nalu_ptr += 2;
177
178 size_t copy_end = nalu_ptr - data + segment_length;
179 if (copy_end > data_size) {
180 delete[] buffer;
philipela75d12d2016-11-07 05:11:28 -0800181 return kDrop;
philipel34852cf2016-11-03 04:03:01 -0700182 }
183
184 memcpy(insert_at, nalu_ptr, segment_length);
185 insert_at += segment_length;
186 nalu_ptr += segment_length;
187 }
188 } else {
johan0d1b2b62017-01-10 04:21:35 -0800189 if (video_header.is_first_packet_in_frame) {
philipel34852cf2016-11-03 04:03:01 -0700190 memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
191 insert_at += sizeof(start_code_h264);
192 }
193 memcpy(insert_at, data, data_size);
194 }
195
196 packet->dataPtr = buffer;
197 packet->sizeBytes = required_size;
philipela75d12d2016-11-07 05:11:28 -0800198 return kInsert;
philipel34852cf2016-11-03 04:03:01 -0700199}
200
johand2b092f2017-01-24 02:38:17 -0800201void H264SpsPpsTracker::InsertSpsPpsNalus(const std::vector<uint8_t>& sps,
202 const std::vector<uint8_t>& pps) {
203 constexpr size_t kNaluHeaderOffset = 1;
204 if (sps.size() < kNaluHeaderOffset) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100205 RTC_LOG(LS_WARNING) << "SPS size " << sps.size() << " is smaller than "
206 << kNaluHeaderOffset;
johand2b092f2017-01-24 02:38:17 -0800207 return;
208 }
209 if ((sps[0] & 0x1f) != H264::NaluType::kSps) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100210 RTC_LOG(LS_WARNING) << "SPS Nalu header missing";
johand2b092f2017-01-24 02:38:17 -0800211 return;
212 }
213 if (pps.size() < kNaluHeaderOffset) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100214 RTC_LOG(LS_WARNING) << "PPS size " << pps.size() << " is smaller than "
215 << kNaluHeaderOffset;
johand2b092f2017-01-24 02:38:17 -0800216 return;
217 }
218 if ((pps[0] & 0x1f) != H264::NaluType::kPps) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100219 RTC_LOG(LS_WARNING) << "SPS Nalu header missing";
johand2b092f2017-01-24 02:38:17 -0800220 return;
221 }
Danil Chapovalov0040b662018-06-18 10:48:16 +0200222 absl::optional<SpsParser::SpsState> parsed_sps = SpsParser::ParseSps(
johand2b092f2017-01-24 02:38:17 -0800223 sps.data() + kNaluHeaderOffset, sps.size() - kNaluHeaderOffset);
Danil Chapovalov0040b662018-06-18 10:48:16 +0200224 absl::optional<PpsParser::PpsState> parsed_pps = PpsParser::ParsePps(
johand2b092f2017-01-24 02:38:17 -0800225 pps.data() + kNaluHeaderOffset, pps.size() - kNaluHeaderOffset);
226
227 if (!parsed_sps) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100228 RTC_LOG(LS_WARNING) << "Failed to parse SPS.";
johand2b092f2017-01-24 02:38:17 -0800229 }
230
231 if (!parsed_pps) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100232 RTC_LOG(LS_WARNING) << "Failed to parse PPS.";
johand2b092f2017-01-24 02:38:17 -0800233 }
philipel022b54e2016-12-20 04:15:59 -0800234
235 if (!parsed_pps || !parsed_sps) {
philipel022b54e2016-12-20 04:15:59 -0800236 return;
237 }
238
239 SpsInfo sps_info;
240 sps_info.size = sps.size();
philipel6585f702017-03-17 06:12:33 -0700241 sps_info.width = parsed_sps->width;
242 sps_info.height = parsed_sps->height;
philipel022b54e2016-12-20 04:15:59 -0800243 uint8_t* sps_data = new uint8_t[sps_info.size];
244 memcpy(sps_data, sps.data(), sps_info.size);
245 sps_info.data.reset(sps_data);
246 sps_data_[parsed_sps->id] = std::move(sps_info);
247
248 PpsInfo pps_info;
249 pps_info.size = pps.size();
250 pps_info.sps_id = parsed_pps->sps_id;
251 uint8_t* pps_data = new uint8_t[pps_info.size];
252 memcpy(pps_data, pps.data(), pps_info.size);
253 pps_info.data.reset(pps_data);
254 pps_data_[parsed_pps->id] = std::move(pps_info);
johand2b092f2017-01-24 02:38:17 -0800255
Mirko Bonadei675513b2017-11-09 11:09:25 +0100256 RTC_LOG(LS_INFO) << "Inserted SPS id " << parsed_sps->id << " and PPS id "
257 << parsed_pps->id << " (referencing SPS "
258 << parsed_pps->sps_id << ")";
philipel022b54e2016-12-20 04:15:59 -0800259}
260
philipel34852cf2016-11-03 04:03:01 -0700261} // namespace video_coding
262} // namespace webrtc