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