1 /*
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
11 #include "webrtc/modules/video_coding/h264_sps_pps_tracker.h"
12
13 #include <string>
14 #include <utility>
15
16 #include "webrtc/base/checks.h"
17 #include "webrtc/base/logging.h"
18 #include "webrtc/common_video/h264/h264_common.h"
19 #include "webrtc/common_video/h264/pps_parser.h"
20 #include "webrtc/common_video/h264/sps_parser.h"
21 #include "webrtc/modules/video_coding/frame_object.h"
22 #include "webrtc/modules/video_coding/packet_buffer.h"
23
24 namespace webrtc {
25 namespace video_coding {
26
27 namespace {
28 const uint8_t start_code_h264[] = {0, 0, 0, 1};
29 } // namespace
30
CopyAndFixBitstream(VCMPacket * packet)31 H264SpsPpsTracker::PacketAction H264SpsPpsTracker::CopyAndFixBitstream(
32 VCMPacket* packet) {
33 RTC_DCHECK(packet->codec == kVideoCodecH264);
34
35 const uint8_t* data = packet->dataPtr;
36 const size_t data_size = packet->sizeBytes;
37 const RTPVideoHeader& video_header = packet->video_header;
38 const RTPVideoHeaderH264& codec_header = video_header.codecHeader.H264;
39
40 // Packets that only contains SPS/PPS are not decodable by themselves, and
41 // to avoid frames being created containing only these two nalus we don't
42 // insert them into the PacketBuffer. Instead we save the SPS/PPS and
43 // prepend the bitstream of first packet of an IDR referring to the
44 // corresponding SPS/PPS id.
45 bool insert_packet = codec_header.nalus_length == 0 ? true : false;
46
47 int pps_id = -1;
48 size_t required_size = 0;
49 for (size_t i = 0; i < codec_header.nalus_length; ++i) {
50 const NaluInfo& nalu = codec_header.nalus[i];
51 switch (nalu.type) {
52 case H264::NaluType::kSps: {
53 // Save SPS.
54 sps_data_[nalu.sps_id].size = nalu.size;
55 sps_data_[nalu.sps_id].data.reset(new uint8_t[nalu.size]);
56 memcpy(sps_data_[nalu.sps_id].data.get(), data + nalu.offset,
57 nalu.size);
58 break;
59 }
60 case H264::NaluType::kPps: {
61 // Save PPS.
62 pps_data_[nalu.pps_id].sps_id = nalu.sps_id;
63 pps_data_[nalu.pps_id].size = nalu.size;
64 pps_data_[nalu.pps_id].data.reset(new uint8_t[nalu.size]);
65 memcpy(pps_data_[nalu.pps_id].data.get(), data + nalu.offset,
66 nalu.size);
67 break;
68 }
69 case H264::NaluType::kIdr: {
70 // If this is the first packet of an IDR, make sure we have the required
71 // SPS/PPS and also calculate how much extra space we need in the buffer
72 // to prepend the SPS/PPS to the bitstream with start codes.
73 if (video_header.is_first_packet_in_frame) {
74 if (nalu.pps_id == -1) {
75 LOG(LS_WARNING) << "No PPS id in IDR nalu.";
76 return kRequestKeyframe;
77 }
78
79 auto pps = pps_data_.find(nalu.pps_id);
80 if (pps == pps_data_.end()) {
81 LOG(LS_WARNING) << "No PPS with id << " << nalu.pps_id
82 << " received";
83 return kRequestKeyframe;
84 }
85
86 auto sps = sps_data_.find(pps->second.sps_id);
87 if (sps == sps_data_.end()) {
88 LOG(LS_WARNING) << "No SPS with id << "
89 << pps_data_[nalu.pps_id].sps_id << " received";
90 return kRequestKeyframe;
91 }
92
93 pps_id = nalu.pps_id;
94 required_size += pps->second.size + sizeof(start_code_h264);
95 required_size += sps->second.size + sizeof(start_code_h264);
96 }
97 FALLTHROUGH();
98 }
99 default: {
100 // Something other than an SPS/PPS nalu in this packet, then it should
101 // be inserted into the PacketBuffer.
102 insert_packet = true;
103 }
104 }
105 }
106
107 if (!insert_packet)
108 return kDrop;
109
110 // Calculate how much space we need for the rest of the bitstream.
111 if (codec_header.packetization_type == kH264StapA) {
112 const uint8_t* nalu_ptr = data + 1;
113 while (nalu_ptr < data + data_size) {
114 RTC_DCHECK(video_header.is_first_packet_in_frame);
115 required_size += sizeof(start_code_h264);
116
117 // The first two bytes describe the length of a segment.
118 uint16_t segment_length = nalu_ptr[0] << 8 | nalu_ptr[1];
119 nalu_ptr += 2;
120
121 required_size += segment_length;
122 nalu_ptr += segment_length;
123 }
124 } else {
125 if (video_header.is_first_packet_in_frame)
126 required_size += sizeof(start_code_h264);
127 required_size += data_size;
128 }
129
130 // Then we copy to the new buffer.
131 uint8_t* buffer = new uint8_t[required_size];
132 uint8_t* insert_at = buffer;
133
134 // If pps_id != -1 then we have the SPS/PPS and they should be prepended
135 // to the bitstream with start codes inserted.
136 if (pps_id != -1) {
137 // Insert SPS.
138 memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
139 insert_at += sizeof(start_code_h264);
140 memcpy(insert_at, sps_data_[pps_data_[pps_id].sps_id].data.get(),
141 sps_data_[pps_data_[pps_id].sps_id].size);
142 insert_at += sps_data_[pps_data_[pps_id].sps_id].size;
143
144 // Insert PPS.
145 memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
146 insert_at += sizeof(start_code_h264);
147 memcpy(insert_at, pps_data_[pps_id].data.get(), pps_data_[pps_id].size);
148 insert_at += pps_data_[pps_id].size;
149 }
150
151 // Copy the rest of the bitstream and insert start codes.
152 if (codec_header.packetization_type == kH264StapA) {
153 const uint8_t* nalu_ptr = data + 1;
154 while (nalu_ptr < data + data_size) {
155 memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
156 insert_at += sizeof(start_code_h264);
157
158 // The first two bytes describe the length of a segment.
159 uint16_t segment_length = nalu_ptr[0] << 8 | nalu_ptr[1];
160 nalu_ptr += 2;
161
162 size_t copy_end = nalu_ptr - data + segment_length;
163 if (copy_end > data_size) {
164 delete[] buffer;
165 return kDrop;
166 }
167
168 memcpy(insert_at, nalu_ptr, segment_length);
169 insert_at += segment_length;
170 nalu_ptr += segment_length;
171 }
172 } else {
173 if (video_header.is_first_packet_in_frame) {
174 memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
175 insert_at += sizeof(start_code_h264);
176 }
177 memcpy(insert_at, data, data_size);
178 }
179
180 packet->dataPtr = buffer;
181 packet->sizeBytes = required_size;
182 return kInsert;
183 }
184
InsertSpsPps(const std::vector<uint8_t> & sps,const std::vector<uint8_t> & pps)185 void H264SpsPpsTracker::InsertSpsPps(const std::vector<uint8_t>& sps,
186 const std::vector<uint8_t>& pps) {
187 rtc::Optional<SpsParser::SpsState> parsed_sps =
188 SpsParser::ParseSps(sps.data(), sps.size());
189 rtc::Optional<PpsParser::PpsState> parsed_pps =
190 PpsParser::ParsePps(pps.data(), pps.size());
191
192 if (!parsed_pps || !parsed_sps) {
193 LOG(LS_WARNING) << "Failed to parse SPS or PPS parameters.";
194 return;
195 }
196
197 SpsInfo sps_info;
198 sps_info.size = sps.size();
199 uint8_t* sps_data = new uint8_t[sps_info.size];
200 memcpy(sps_data, sps.data(), sps_info.size);
201 sps_info.data.reset(sps_data);
202 sps_data_[parsed_sps->id] = std::move(sps_info);
203
204 PpsInfo pps_info;
205 pps_info.size = pps.size();
206 pps_info.sps_id = parsed_pps->sps_id;
207 uint8_t* pps_data = new uint8_t[pps_info.size];
208 memcpy(pps_data, pps.data(), pps_info.size);
209 pps_info.data.reset(pps_data);
210 pps_data_[parsed_pps->id] = std::move(pps_info);
211 }
212
213 } // namespace video_coding
214 } // namespace webrtc
215