Line data Source code
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 :
31 0 : H264SpsPpsTracker::PacketAction H264SpsPpsTracker::CopyAndFixBitstream(
32 : VCMPacket* packet) {
33 0 : RTC_DCHECK(packet->codec == kVideoCodecH264);
34 :
35 0 : const uint8_t* data = packet->dataPtr;
36 0 : const size_t data_size = packet->sizeBytes;
37 0 : const RTPVideoHeader& video_header = packet->video_header;
38 0 : 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 0 : bool insert_packet = codec_header.nalus_length == 0 ? true : false;
46 :
47 0 : int pps_id = -1;
48 0 : size_t required_size = 0;
49 0 : for (size_t i = 0; i < codec_header.nalus_length; ++i) {
50 0 : const NaluInfo& nalu = codec_header.nalus[i];
51 0 : switch (nalu.type) {
52 : case H264::NaluType::kSps: {
53 : // Save SPS.
54 0 : sps_data_[nalu.sps_id].size = nalu.size;
55 0 : sps_data_[nalu.sps_id].data.reset(new uint8_t[nalu.size]);
56 0 : memcpy(sps_data_[nalu.sps_id].data.get(), data + nalu.offset,
57 0 : nalu.size);
58 0 : break;
59 : }
60 : case H264::NaluType::kPps: {
61 : // Save PPS.
62 0 : pps_data_[nalu.pps_id].sps_id = nalu.sps_id;
63 0 : pps_data_[nalu.pps_id].size = nalu.size;
64 0 : pps_data_[nalu.pps_id].data.reset(new uint8_t[nalu.size]);
65 0 : memcpy(pps_data_[nalu.pps_id].data.get(), data + nalu.offset,
66 0 : nalu.size);
67 0 : 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 0 : if (video_header.is_first_packet_in_frame) {
74 0 : if (nalu.pps_id == -1) {
75 0 : LOG(LS_WARNING) << "No PPS id in IDR nalu.";
76 0 : return kRequestKeyframe;
77 : }
78 :
79 0 : auto pps = pps_data_.find(nalu.pps_id);
80 0 : if (pps == pps_data_.end()) {
81 0 : LOG(LS_WARNING) << "No PPS with id << " << nalu.pps_id
82 0 : << " received";
83 0 : return kRequestKeyframe;
84 : }
85 :
86 0 : auto sps = sps_data_.find(pps->second.sps_id);
87 0 : if (sps == sps_data_.end()) {
88 0 : LOG(LS_WARNING) << "No SPS with id << "
89 0 : << pps_data_[nalu.pps_id].sps_id << " received";
90 0 : return kRequestKeyframe;
91 : }
92 :
93 0 : pps_id = nalu.pps_id;
94 0 : required_size += pps->second.size + sizeof(start_code_h264);
95 0 : 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 0 : insert_packet = true;
103 : }
104 : }
105 : }
106 :
107 0 : if (!insert_packet)
108 0 : return kDrop;
109 :
110 : // Calculate how much space we need for the rest of the bitstream.
111 0 : if (codec_header.packetization_type == kH264StapA) {
112 0 : const uint8_t* nalu_ptr = data + 1;
113 0 : while (nalu_ptr < data + data_size) {
114 0 : RTC_DCHECK(video_header.is_first_packet_in_frame);
115 0 : required_size += sizeof(start_code_h264);
116 :
117 : // The first two bytes describe the length of a segment.
118 0 : uint16_t segment_length = nalu_ptr[0] << 8 | nalu_ptr[1];
119 0 : nalu_ptr += 2;
120 :
121 0 : required_size += segment_length;
122 0 : nalu_ptr += segment_length;
123 : }
124 : } else {
125 0 : if (video_header.is_first_packet_in_frame)
126 0 : required_size += sizeof(start_code_h264);
127 0 : required_size += data_size;
128 : }
129 :
130 : // Then we copy to the new buffer.
131 0 : uint8_t* buffer = new uint8_t[required_size];
132 0 : 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 0 : if (pps_id != -1) {
137 : // Insert SPS.
138 0 : memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
139 0 : insert_at += sizeof(start_code_h264);
140 0 : memcpy(insert_at, sps_data_[pps_data_[pps_id].sps_id].data.get(),
141 0 : sps_data_[pps_data_[pps_id].sps_id].size);
142 0 : insert_at += sps_data_[pps_data_[pps_id].sps_id].size;
143 :
144 : // Insert PPS.
145 0 : memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
146 0 : insert_at += sizeof(start_code_h264);
147 0 : memcpy(insert_at, pps_data_[pps_id].data.get(), pps_data_[pps_id].size);
148 0 : insert_at += pps_data_[pps_id].size;
149 : }
150 :
151 : // Copy the rest of the bitstream and insert start codes.
152 0 : if (codec_header.packetization_type == kH264StapA) {
153 0 : const uint8_t* nalu_ptr = data + 1;
154 0 : while (nalu_ptr < data + data_size) {
155 0 : memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
156 0 : insert_at += sizeof(start_code_h264);
157 :
158 : // The first two bytes describe the length of a segment.
159 0 : uint16_t segment_length = nalu_ptr[0] << 8 | nalu_ptr[1];
160 0 : nalu_ptr += 2;
161 :
162 0 : size_t copy_end = nalu_ptr - data + segment_length;
163 0 : if (copy_end > data_size) {
164 0 : delete[] buffer;
165 0 : return kDrop;
166 : }
167 :
168 0 : memcpy(insert_at, nalu_ptr, segment_length);
169 0 : insert_at += segment_length;
170 0 : nalu_ptr += segment_length;
171 : }
172 : } else {
173 0 : if (video_header.is_first_packet_in_frame) {
174 0 : memcpy(insert_at, start_code_h264, sizeof(start_code_h264));
175 0 : insert_at += sizeof(start_code_h264);
176 : }
177 0 : memcpy(insert_at, data, data_size);
178 : }
179 :
180 0 : packet->dataPtr = buffer;
181 0 : packet->sizeBytes = required_size;
182 0 : return kInsert;
183 : }
184 :
185 0 : void H264SpsPpsTracker::InsertSpsPps(const std::vector<uint8_t>& sps,
186 : const std::vector<uint8_t>& pps) {
187 : rtc::Optional<SpsParser::SpsState> parsed_sps =
188 0 : SpsParser::ParseSps(sps.data(), sps.size());
189 : rtc::Optional<PpsParser::PpsState> parsed_pps =
190 0 : PpsParser::ParsePps(pps.data(), pps.size());
191 :
192 0 : if (!parsed_pps || !parsed_sps) {
193 0 : LOG(LS_WARNING) << "Failed to parse SPS or PPS parameters.";
194 0 : return;
195 : }
196 :
197 0 : SpsInfo sps_info;
198 0 : sps_info.size = sps.size();
199 0 : uint8_t* sps_data = new uint8_t[sps_info.size];
200 0 : memcpy(sps_data, sps.data(), sps_info.size);
201 0 : sps_info.data.reset(sps_data);
202 0 : sps_data_[parsed_sps->id] = std::move(sps_info);
203 :
204 0 : PpsInfo pps_info;
205 0 : pps_info.size = pps.size();
206 0 : pps_info.sps_id = parsed_pps->sps_id;
207 0 : uint8_t* pps_data = new uint8_t[pps_info.size];
208 0 : memcpy(pps_data, pps.data(), pps_info.size);
209 0 : pps_info.data.reset(pps_data);
210 0 : pps_data_[parsed_pps->id] = std::move(pps_info);
211 : }
212 :
213 : } // namespace video_coding
214 : } // namespace webrtc
|