OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 * | 9 * |
10 */ | 10 */ |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
154 CFRelease(contiguous_buffer); | 154 CFRelease(contiguous_buffer); |
155 return true; | 155 return true; |
156 } | 156 } |
157 | 157 |
158 bool H264AnnexBBufferToCMSampleBuffer(const uint8_t* annexb_buffer, | 158 bool H264AnnexBBufferToCMSampleBuffer(const uint8_t* annexb_buffer, |
159 size_t annexb_buffer_size, | 159 size_t annexb_buffer_size, |
160 CMVideoFormatDescriptionRef video_format, | 160 CMVideoFormatDescriptionRef video_format, |
161 CMSampleBufferRef* out_sample_buffer) { | 161 CMSampleBufferRef* out_sample_buffer) { |
162 RTC_DCHECK(annexb_buffer); | 162 RTC_DCHECK(annexb_buffer); |
163 RTC_DCHECK(out_sample_buffer); | 163 RTC_DCHECK(out_sample_buffer); |
| 164 RTC_DCHECK(video_format); |
164 *out_sample_buffer = nullptr; | 165 *out_sample_buffer = nullptr; |
165 | 166 |
166 // The buffer we receive via RTP has 00 00 00 01 start code artifically | |
167 // embedded by the RTP depacketizer. Extract NALU information. | |
168 // TODO(tkchin): handle potential case where sps and pps are delivered | |
169 // separately. | |
170 uint8_t first_nalu_type = annexb_buffer[4] & 0x1f; | |
171 bool is_first_nalu_type_sps = first_nalu_type == 0x7; | |
172 | |
173 AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size); | 167 AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size); |
174 CMVideoFormatDescriptionRef description = nullptr; | 168 if (H264AnnexBBufferHasVideoFormatDescription(annexb_buffer, |
175 OSStatus status = noErr; | 169 annexb_buffer_size)) { |
176 if (is_first_nalu_type_sps) { | 170 // Advance past the SPS and PPS. |
177 // Parse the SPS and PPS into a CMVideoFormatDescription. | 171 const uint8_t* data = nullptr; |
178 const uint8_t* param_set_ptrs[2] = {}; | 172 size_t data_len = 0; |
179 size_t param_set_sizes[2] = {}; | 173 if (!reader.ReadNalu(&data, &data_len)) { |
180 if (!reader.ReadNalu(¶m_set_ptrs[0], ¶m_set_sizes[0])) { | |
181 LOG(LS_ERROR) << "Failed to read SPS"; | 174 LOG(LS_ERROR) << "Failed to read SPS"; |
182 return false; | 175 return false; |
183 } | 176 } |
184 if (!reader.ReadNalu(¶m_set_ptrs[1], ¶m_set_sizes[1])) { | 177 if (!reader.ReadNalu(&data, &data_len)) { |
185 LOG(LS_ERROR) << "Failed to read PPS"; | 178 LOG(LS_ERROR) << "Failed to read PPS"; |
186 return false; | 179 return false; |
187 } | 180 } |
188 status = CMVideoFormatDescriptionCreateFromH264ParameterSets( | |
189 kCFAllocatorDefault, 2, param_set_ptrs, param_set_sizes, 4, | |
190 &description); | |
191 if (status != noErr) { | |
192 LOG(LS_ERROR) << "Failed to create video format description."; | |
193 return false; | |
194 } | |
195 } else { | |
196 RTC_DCHECK(video_format); | |
197 description = video_format; | |
198 // We don't need to retain, but it makes logic easier since we are creating | |
199 // in the other block. | |
200 CFRetain(description); | |
201 } | 181 } |
202 | 182 |
203 // Allocate memory as a block buffer. | 183 // Allocate memory as a block buffer. |
204 // TODO(tkchin): figure out how to use a pool. | 184 // TODO(tkchin): figure out how to use a pool. |
205 CMBlockBufferRef block_buffer = nullptr; | 185 CMBlockBufferRef block_buffer = nullptr; |
206 status = CMBlockBufferCreateWithMemoryBlock( | 186 OSStatus status = CMBlockBufferCreateWithMemoryBlock( |
207 nullptr, nullptr, reader.BytesRemaining(), nullptr, nullptr, 0, | 187 nullptr, nullptr, reader.BytesRemaining(), nullptr, nullptr, 0, |
208 reader.BytesRemaining(), kCMBlockBufferAssureMemoryNowFlag, | 188 reader.BytesRemaining(), kCMBlockBufferAssureMemoryNowFlag, |
209 &block_buffer); | 189 &block_buffer); |
210 if (status != kCMBlockBufferNoErr) { | 190 if (status != kCMBlockBufferNoErr) { |
211 LOG(LS_ERROR) << "Failed to create block buffer."; | 191 LOG(LS_ERROR) << "Failed to create block buffer."; |
212 CFRelease(description); | |
213 return false; | 192 return false; |
214 } | 193 } |
215 | 194 |
216 // Make sure block buffer is contiguous. | 195 // Make sure block buffer is contiguous. |
217 CMBlockBufferRef contiguous_buffer = nullptr; | 196 CMBlockBufferRef contiguous_buffer = nullptr; |
218 if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) { | 197 if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) { |
219 status = CMBlockBufferCreateContiguous( | 198 status = CMBlockBufferCreateContiguous( |
220 nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer); | 199 nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer); |
221 if (status != noErr) { | 200 if (status != noErr) { |
222 LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: " | 201 LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: " |
223 << status; | 202 << status; |
224 CFRelease(description); | |
225 CFRelease(block_buffer); | 203 CFRelease(block_buffer); |
226 return false; | 204 return false; |
227 } | 205 } |
228 } else { | 206 } else { |
229 contiguous_buffer = block_buffer; | 207 contiguous_buffer = block_buffer; |
230 block_buffer = nullptr; | 208 block_buffer = nullptr; |
231 } | 209 } |
232 | 210 |
233 // Get a raw pointer into allocated memory. | 211 // Get a raw pointer into allocated memory. |
234 size_t block_buffer_size = 0; | 212 size_t block_buffer_size = 0; |
235 char* data_ptr = nullptr; | 213 char* data_ptr = nullptr; |
236 status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr, | 214 status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr, |
237 &block_buffer_size, &data_ptr); | 215 &block_buffer_size, &data_ptr); |
238 if (status != kCMBlockBufferNoErr) { | 216 if (status != kCMBlockBufferNoErr) { |
239 LOG(LS_ERROR) << "Failed to get block buffer data pointer."; | 217 LOG(LS_ERROR) << "Failed to get block buffer data pointer."; |
240 CFRelease(description); | |
241 CFRelease(contiguous_buffer); | 218 CFRelease(contiguous_buffer); |
242 return false; | 219 return false; |
243 } | 220 } |
244 RTC_DCHECK(block_buffer_size == reader.BytesRemaining()); | 221 RTC_DCHECK(block_buffer_size == reader.BytesRemaining()); |
245 | 222 |
246 // Write Avcc NALUs into block buffer memory. | 223 // Write Avcc NALUs into block buffer memory. |
247 AvccBufferWriter writer(reinterpret_cast<uint8_t*>(data_ptr), | 224 AvccBufferWriter writer(reinterpret_cast<uint8_t*>(data_ptr), |
248 block_buffer_size); | 225 block_buffer_size); |
249 while (reader.BytesRemaining() > 0) { | 226 while (reader.BytesRemaining() > 0) { |
250 const uint8_t* nalu_data_ptr = nullptr; | 227 const uint8_t* nalu_data_ptr = nullptr; |
251 size_t nalu_data_size = 0; | 228 size_t nalu_data_size = 0; |
252 if (reader.ReadNalu(&nalu_data_ptr, &nalu_data_size)) { | 229 if (reader.ReadNalu(&nalu_data_ptr, &nalu_data_size)) { |
253 writer.WriteNalu(nalu_data_ptr, nalu_data_size); | 230 writer.WriteNalu(nalu_data_ptr, nalu_data_size); |
254 } | 231 } |
255 } | 232 } |
256 | 233 |
257 // Create sample buffer. | 234 // Create sample buffer. |
258 status = CMSampleBufferCreate(nullptr, contiguous_buffer, true, nullptr, | 235 status = CMSampleBufferCreate(nullptr, contiguous_buffer, true, nullptr, |
259 nullptr, description, 1, 0, nullptr, 0, nullptr, | 236 nullptr, video_format, 1, 0, nullptr, 0, |
260 out_sample_buffer); | 237 nullptr, out_sample_buffer); |
261 if (status != noErr) { | 238 if (status != noErr) { |
262 LOG(LS_ERROR) << "Failed to create sample buffer."; | 239 LOG(LS_ERROR) << "Failed to create sample buffer."; |
263 CFRelease(description); | |
264 CFRelease(contiguous_buffer); | 240 CFRelease(contiguous_buffer); |
265 return false; | 241 return false; |
266 } | 242 } |
267 CFRelease(description); | |
268 CFRelease(contiguous_buffer); | 243 CFRelease(contiguous_buffer); |
269 return true; | 244 return true; |
270 } | 245 } |
271 | 246 |
| 247 bool H264AnnexBBufferHasVideoFormatDescription(const uint8_t* annexb_buffer, |
| 248 size_t annexb_buffer_size) { |
| 249 RTC_DCHECK(annexb_buffer); |
| 250 RTC_DCHECK_GT(annexb_buffer_size, 4u); |
| 251 |
| 252 // The buffer we receive via RTP has 00 00 00 01 start code artifically |
| 253 // embedded by the RTP depacketizer. Extract NALU information. |
| 254 // TODO(tkchin): handle potential case where sps and pps are delivered |
| 255 // separately. |
| 256 uint8_t first_nalu_type = annexb_buffer[4] & 0x1f; |
| 257 bool is_first_nalu_type_sps = first_nalu_type == 0x7; |
| 258 return is_first_nalu_type_sps; |
| 259 } |
| 260 |
| 261 CMVideoFormatDescriptionRef CreateVideoFormatDescription( |
| 262 const uint8_t* annexb_buffer, |
| 263 size_t annexb_buffer_size) { |
| 264 if (!H264AnnexBBufferHasVideoFormatDescription(annexb_buffer, |
| 265 annexb_buffer_size)) { |
| 266 return nullptr; |
| 267 } |
| 268 AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size); |
| 269 CMVideoFormatDescriptionRef description = nullptr; |
| 270 OSStatus status = noErr; |
| 271 // Parse the SPS and PPS into a CMVideoFormatDescription. |
| 272 const uint8_t* param_set_ptrs[2] = {}; |
| 273 size_t param_set_sizes[2] = {}; |
| 274 if (!reader.ReadNalu(¶m_set_ptrs[0], ¶m_set_sizes[0])) { |
| 275 LOG(LS_ERROR) << "Failed to read SPS"; |
| 276 return nullptr; |
| 277 } |
| 278 if (!reader.ReadNalu(¶m_set_ptrs[1], ¶m_set_sizes[1])) { |
| 279 LOG(LS_ERROR) << "Failed to read PPS"; |
| 280 return nullptr; |
| 281 } |
| 282 status = CMVideoFormatDescriptionCreateFromH264ParameterSets( |
| 283 kCFAllocatorDefault, 2, param_set_ptrs, param_set_sizes, 4, |
| 284 &description); |
| 285 if (status != noErr) { |
| 286 LOG(LS_ERROR) << "Failed to create video format description."; |
| 287 return nullptr; |
| 288 } |
| 289 return description; |
| 290 } |
| 291 |
272 AnnexBBufferReader::AnnexBBufferReader(const uint8_t* annexb_buffer, | 292 AnnexBBufferReader::AnnexBBufferReader(const uint8_t* annexb_buffer, |
273 size_t length) | 293 size_t length) |
274 : start_(annexb_buffer), offset_(0), next_offset_(0), length_(length) { | 294 : start_(annexb_buffer), offset_(0), next_offset_(0), length_(length) { |
275 RTC_DCHECK(annexb_buffer); | 295 RTC_DCHECK(annexb_buffer); |
276 offset_ = FindNextNaluHeader(start_, length_, 0); | 296 offset_ = FindNextNaluHeader(start_, length_, 0); |
277 next_offset_ = | 297 next_offset_ = |
278 FindNextNaluHeader(start_, length_, offset_ + sizeof(kAnnexBHeaderBytes)); | 298 FindNextNaluHeader(start_, length_, offset_ + sizeof(kAnnexBHeaderBytes)); |
279 } | 299 } |
280 | 300 |
281 bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu, | 301 bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu, |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
347 return true; | 367 return true; |
348 } | 368 } |
349 | 369 |
350 size_t AvccBufferWriter::BytesRemaining() const { | 370 size_t AvccBufferWriter::BytesRemaining() const { |
351 return length_ - offset_; | 371 return length_ - offset_; |
352 } | 372 } |
353 | 373 |
354 } // namespace webrtc | 374 } // namespace webrtc |
355 | 375 |
356 #endif // defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED) | 376 #endif // defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED) |
OLD | NEW |