blob: 55ed1c2b4ca3700e0804ade2cbbea0e807b1e5ab [file] [log] [blame]
Zeke Chin71f6f442015-06-29 14:34:58 -07001/*
2 * Copyright (c) 2015 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
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020012#include "sdk/objc/components/video_codec/nalu_rewriter.h"
Zeke Chin71f6f442015-06-29 14:34:58 -070013
14#include <CoreFoundation/CoreFoundation.h>
kwiberg3f55dea2016-02-29 05:51:59 -080015#include <memory>
Zeke Chin71f6f442015-06-29 14:34:58 -070016#include <vector>
17
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/checks.h"
19#include "rtc_base/logging.h"
Zeke Chin71f6f442015-06-29 14:34:58 -070020
21namespace webrtc {
22
jianjun.zhue1b4d242016-08-15 18:56:18 -070023using H264::kAud;
24using H264::kSps;
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +010025using H264::NaluIndex;
26using H264::NaluType;
jianjun.zhue1b4d242016-08-15 18:56:18 -070027using H264::ParseNaluType;
28
Zeke Chin71f6f442015-06-29 14:34:58 -070029const char kAnnexBHeaderBytes[4] = {0, 0, 0, 1};
30const size_t kAvccHeaderByteSize = sizeof(uint32_t);
31
32bool H264CMSampleBufferToAnnexBBuffer(
33 CMSampleBufferRef avcc_sample_buffer,
34 bool is_keyframe,
35 rtc::Buffer* annexb_buffer,
Yves Gerey665174f2018-06-19 15:03:05 +020036 std::unique_ptr<RTPFragmentationHeader>* out_header) {
henrikg91d6ede2015-09-17 00:24:34 -070037 RTC_DCHECK(avcc_sample_buffer);
38 RTC_DCHECK(out_header);
kthelgasonf8084d42017-08-30 04:47:10 -070039 out_header->reset(nullptr);
Zeke Chin71f6f442015-06-29 14:34:58 -070040
41 // Get format description from the sample buffer.
42 CMVideoFormatDescriptionRef description =
43 CMSampleBufferGetFormatDescription(avcc_sample_buffer);
44 if (description == nullptr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010045 RTC_LOG(LS_ERROR) << "Failed to get sample buffer's description.";
Zeke Chin71f6f442015-06-29 14:34:58 -070046 return false;
47 }
48
49 // Get parameter set information.
50 int nalu_header_size = 0;
51 size_t param_set_count = 0;
52 OSStatus status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
53 description, 0, nullptr, nullptr, &param_set_count, &nalu_header_size);
54 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010055 RTC_LOG(LS_ERROR) << "Failed to get parameter set.";
Zeke Chin71f6f442015-06-29 14:34:58 -070056 return false;
57 }
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +010058 RTC_CHECK_EQ(nalu_header_size, kAvccHeaderByteSize);
kwibergaf476c72016-11-28 15:21:39 -080059 RTC_DCHECK_EQ(param_set_count, 2);
Zeke Chin71f6f442015-06-29 14:34:58 -070060
61 // Truncate any previous data in the buffer without changing its capacity.
62 annexb_buffer->SetSize(0);
63
64 size_t nalu_offset = 0;
65 std::vector<size_t> frag_offsets;
66 std::vector<size_t> frag_lengths;
67
68 // Place all parameter sets at the front of buffer.
69 if (is_keyframe) {
70 size_t param_set_size = 0;
71 const uint8_t* param_set = nullptr;
72 for (size_t i = 0; i < param_set_count; ++i) {
73 status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
74 description, i, &param_set, &param_set_size, nullptr, nullptr);
75 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010076 RTC_LOG(LS_ERROR) << "Failed to get parameter set.";
Zeke Chin71f6f442015-06-29 14:34:58 -070077 return false;
78 }
79 // Update buffer.
80 annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes));
81 annexb_buffer->AppendData(reinterpret_cast<const char*>(param_set),
82 param_set_size);
83 // Update fragmentation.
84 frag_offsets.push_back(nalu_offset + sizeof(kAnnexBHeaderBytes));
85 frag_lengths.push_back(param_set_size);
86 nalu_offset += sizeof(kAnnexBHeaderBytes) + param_set_size;
87 }
88 }
89
90 // Get block buffer from the sample buffer.
91 CMBlockBufferRef block_buffer =
92 CMSampleBufferGetDataBuffer(avcc_sample_buffer);
93 if (block_buffer == nullptr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010094 RTC_LOG(LS_ERROR) << "Failed to get sample buffer's block buffer.";
Zeke Chin71f6f442015-06-29 14:34:58 -070095 return false;
96 }
97 CMBlockBufferRef contiguous_buffer = nullptr;
98 // Make sure block buffer is contiguous.
99 if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) {
100 status = CMBlockBufferCreateContiguous(
101 nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer);
102 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100103 RTC_LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: "
104 << status;
Zeke Chin71f6f442015-06-29 14:34:58 -0700105 return false;
106 }
107 } else {
108 contiguous_buffer = block_buffer;
109 // Retain to make cleanup easier.
110 CFRetain(contiguous_buffer);
111 block_buffer = nullptr;
112 }
113
114 // Now copy the actual data.
115 char* data_ptr = nullptr;
116 size_t block_buffer_size = CMBlockBufferGetDataLength(contiguous_buffer);
117 status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr, nullptr,
118 &data_ptr);
119 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100120 RTC_LOG(LS_ERROR) << "Failed to get block buffer data.";
Zeke Chin71f6f442015-06-29 14:34:58 -0700121 CFRelease(contiguous_buffer);
122 return false;
123 }
124 size_t bytes_remaining = block_buffer_size;
125 while (bytes_remaining > 0) {
126 // The size type here must match |nalu_header_size|, we expect 4 bytes.
127 // Read the length of the next packet of data. Must convert from big endian
128 // to host endian.
henrikg91d6ede2015-09-17 00:24:34 -0700129 RTC_DCHECK_GE(bytes_remaining, (size_t)nalu_header_size);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200130 uint32_t* uint32_data_ptr = reinterpret_cast<uint32_t*>(data_ptr);
Zeke Chin71f6f442015-06-29 14:34:58 -0700131 uint32_t packet_size = CFSwapInt32BigToHost(*uint32_data_ptr);
132 // Update buffer.
133 annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes));
134 annexb_buffer->AppendData(data_ptr + nalu_header_size, packet_size);
135 // Update fragmentation.
136 frag_offsets.push_back(nalu_offset + sizeof(kAnnexBHeaderBytes));
137 frag_lengths.push_back(packet_size);
138 nalu_offset += sizeof(kAnnexBHeaderBytes) + packet_size;
139
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100140 size_t bytes_written = packet_size + sizeof(kAnnexBHeaderBytes);
Zeke Chin71f6f442015-06-29 14:34:58 -0700141 bytes_remaining -= bytes_written;
142 data_ptr += bytes_written;
143 }
henrikg91d6ede2015-09-17 00:24:34 -0700144 RTC_DCHECK_EQ(bytes_remaining, (size_t)0);
Zeke Chin71f6f442015-06-29 14:34:58 -0700145
kthelgasonf8084d42017-08-30 04:47:10 -0700146 std::unique_ptr<RTPFragmentationHeader> header(new RTPFragmentationHeader());
Zeke Chin71f6f442015-06-29 14:34:58 -0700147 header->VerifyAndAllocateFragmentationHeader(frag_offsets.size());
henrikg91d6ede2015-09-17 00:24:34 -0700148 RTC_DCHECK_EQ(frag_lengths.size(), frag_offsets.size());
Zeke Chin71f6f442015-06-29 14:34:58 -0700149 for (size_t i = 0; i < frag_offsets.size(); ++i) {
150 header->fragmentationOffset[i] = frag_offsets[i];
151 header->fragmentationLength[i] = frag_lengths[i];
152 header->fragmentationPlType[i] = 0;
153 header->fragmentationTimeDiff[i] = 0;
154 }
kthelgasonf8084d42017-08-30 04:47:10 -0700155 *out_header = std::move(header);
Zeke Chin71f6f442015-06-29 14:34:58 -0700156 CFRelease(contiguous_buffer);
157 return true;
158}
159
philipelcce46fc2015-12-21 03:04:49 -0800160bool H264AnnexBBufferToCMSampleBuffer(const uint8_t* annexb_buffer,
161 size_t annexb_buffer_size,
162 CMVideoFormatDescriptionRef video_format,
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200163 CMSampleBufferRef* out_sample_buffer,
164 CMMemoryPoolRef memory_pool) {
henrikg91d6ede2015-09-17 00:24:34 -0700165 RTC_DCHECK(annexb_buffer);
166 RTC_DCHECK(out_sample_buffer);
tkchin5ed5ed92016-03-08 10:51:54 -0800167 RTC_DCHECK(video_format);
Zeke Chin71f6f442015-06-29 14:34:58 -0700168 *out_sample_buffer = nullptr;
169
Zeke Chin71f6f442015-06-29 14:34:58 -0700170 AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size);
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800171 if (reader.SeekToNextNaluOfType(kSps)) {
172 // Buffer contains an SPS NALU - skip it and the following PPS
173 const uint8_t* data;
174 size_t data_len;
tkchin5ed5ed92016-03-08 10:51:54 -0800175 if (!reader.ReadNalu(&data, &data_len)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100176 RTC_LOG(LS_ERROR) << "Failed to read SPS";
Zeke Chin71f6f442015-06-29 14:34:58 -0700177 return false;
178 }
tkchin5ed5ed92016-03-08 10:51:54 -0800179 if (!reader.ReadNalu(&data, &data_len)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100180 RTC_LOG(LS_ERROR) << "Failed to read PPS";
Zeke Chin71f6f442015-06-29 14:34:58 -0700181 return false;
182 }
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800183 } else {
184 // No SPS NALU - start reading from the first NALU in the buffer
185 reader.SeekToStart();
Zeke Chin71f6f442015-06-29 14:34:58 -0700186 }
187
188 // Allocate memory as a block buffer.
Zeke Chin71f6f442015-06-29 14:34:58 -0700189 CMBlockBufferRef block_buffer = nullptr;
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200190 CFAllocatorRef block_allocator = CMMemoryPoolGetAllocator(memory_pool);
tkchin5ed5ed92016-03-08 10:51:54 -0800191 OSStatus status = CMBlockBufferCreateWithMemoryBlock(
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200192 kCFAllocatorDefault, nullptr, reader.BytesRemaining(), block_allocator,
193 nullptr, 0, reader.BytesRemaining(), kCMBlockBufferAssureMemoryNowFlag,
Zeke Chin71f6f442015-06-29 14:34:58 -0700194 &block_buffer);
195 if (status != kCMBlockBufferNoErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100196 RTC_LOG(LS_ERROR) << "Failed to create block buffer.";
Zeke Chin71f6f442015-06-29 14:34:58 -0700197 return false;
198 }
199
200 // Make sure block buffer is contiguous.
201 CMBlockBufferRef contiguous_buffer = nullptr;
202 if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) {
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200203 status = CMBlockBufferCreateContiguous(kCFAllocatorDefault, block_buffer,
204 block_allocator, nullptr, 0, 0, 0,
205 &contiguous_buffer);
Zeke Chin71f6f442015-06-29 14:34:58 -0700206 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100207 RTC_LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: "
208 << status;
Zeke Chin71f6f442015-06-29 14:34:58 -0700209 CFRelease(block_buffer);
210 return false;
211 }
212 } else {
213 contiguous_buffer = block_buffer;
214 block_buffer = nullptr;
215 }
216
217 // Get a raw pointer into allocated memory.
218 size_t block_buffer_size = 0;
219 char* data_ptr = nullptr;
220 status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr,
221 &block_buffer_size, &data_ptr);
222 if (status != kCMBlockBufferNoErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100223 RTC_LOG(LS_ERROR) << "Failed to get block buffer data pointer.";
Zeke Chin71f6f442015-06-29 14:34:58 -0700224 CFRelease(contiguous_buffer);
225 return false;
226 }
henrikg91d6ede2015-09-17 00:24:34 -0700227 RTC_DCHECK(block_buffer_size == reader.BytesRemaining());
Zeke Chin71f6f442015-06-29 14:34:58 -0700228
229 // Write Avcc NALUs into block buffer memory.
230 AvccBufferWriter writer(reinterpret_cast<uint8_t*>(data_ptr),
231 block_buffer_size);
232 while (reader.BytesRemaining() > 0) {
233 const uint8_t* nalu_data_ptr = nullptr;
234 size_t nalu_data_size = 0;
235 if (reader.ReadNalu(&nalu_data_ptr, &nalu_data_size)) {
236 writer.WriteNalu(nalu_data_ptr, nalu_data_size);
237 }
238 }
239
240 // Create sample buffer.
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200241 status = CMSampleBufferCreate(kCFAllocatorDefault, contiguous_buffer, true,
242 nullptr, nullptr, video_format, 1, 0, nullptr,
243 0, nullptr, out_sample_buffer);
Zeke Chin71f6f442015-06-29 14:34:58 -0700244 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100245 RTC_LOG(LS_ERROR) << "Failed to create sample buffer.";
Zeke Chin71f6f442015-06-29 14:34:58 -0700246 CFRelease(contiguous_buffer);
247 return false;
248 }
Zeke Chin71f6f442015-06-29 14:34:58 -0700249 CFRelease(contiguous_buffer);
250 return true;
251}
252
tkchin5ed5ed92016-03-08 10:51:54 -0800253CMVideoFormatDescriptionRef CreateVideoFormatDescription(
254 const uint8_t* annexb_buffer,
255 size_t annexb_buffer_size) {
tkchin5ed5ed92016-03-08 10:51:54 -0800256 const uint8_t* param_set_ptrs[2] = {};
257 size_t param_set_sizes[2] = {};
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800258 AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size);
259 // Skip everyting before the SPS, then read the SPS and PPS
260 if (!reader.SeekToNextNaluOfType(kSps)) {
261 return nullptr;
jianjun.zhue1b4d242016-08-15 18:56:18 -0700262 }
tkchin5ed5ed92016-03-08 10:51:54 -0800263 if (!reader.ReadNalu(&param_set_ptrs[0], &param_set_sizes[0])) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100264 RTC_LOG(LS_ERROR) << "Failed to read SPS";
tkchin5ed5ed92016-03-08 10:51:54 -0800265 return nullptr;
266 }
267 if (!reader.ReadNalu(&param_set_ptrs[1], &param_set_sizes[1])) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100268 RTC_LOG(LS_ERROR) << "Failed to read PPS";
tkchin5ed5ed92016-03-08 10:51:54 -0800269 return nullptr;
270 }
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800271
272 // Parse the SPS and PPS into a CMVideoFormatDescription.
273 CMVideoFormatDescriptionRef description = nullptr;
274 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
275 kCFAllocatorDefault, 2, param_set_ptrs, param_set_sizes, 4, &description);
tkchin5ed5ed92016-03-08 10:51:54 -0800276 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100277 RTC_LOG(LS_ERROR) << "Failed to create video format description.";
tkchin5ed5ed92016-03-08 10:51:54 -0800278 return nullptr;
279 }
280 return description;
281}
282
Zeke Chin71f6f442015-06-29 14:34:58 -0700283AnnexBBufferReader::AnnexBBufferReader(const uint8_t* annexb_buffer,
284 size_t length)
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100285 : start_(annexb_buffer), length_(length) {
henrikg91d6ede2015-09-17 00:24:34 -0700286 RTC_DCHECK(annexb_buffer);
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100287 offsets_ = H264::FindNaluIndices(annexb_buffer, length);
288 offset_ = offsets_.begin();
Zeke Chin71f6f442015-06-29 14:34:58 -0700289}
290
Mirko Bonadei17aff352018-07-26 12:20:40 +0200291AnnexBBufferReader::~AnnexBBufferReader() = default;
292
Zeke Chin71f6f442015-06-29 14:34:58 -0700293bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu,
294 size_t* out_length) {
henrikg91d6ede2015-09-17 00:24:34 -0700295 RTC_DCHECK(out_nalu);
296 RTC_DCHECK(out_length);
Zeke Chin71f6f442015-06-29 14:34:58 -0700297 *out_nalu = nullptr;
298 *out_length = 0;
299
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100300 if (offset_ == offsets_.end()) {
Zeke Chin71f6f442015-06-29 14:34:58 -0700301 return false;
302 }
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100303 *out_nalu = start_ + offset_->payload_start_offset;
304 *out_length = offset_->payload_size;
305 ++offset_;
Zeke Chin71f6f442015-06-29 14:34:58 -0700306 return true;
307}
308
309size_t AnnexBBufferReader::BytesRemaining() const {
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100310 if (offset_ == offsets_.end()) {
311 return 0;
Zeke Chin71f6f442015-06-29 14:34:58 -0700312 }
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100313 return length_ - offset_->start_offset;
Zeke Chin71f6f442015-06-29 14:34:58 -0700314}
315
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800316void AnnexBBufferReader::SeekToStart() {
317 offset_ = offsets_.begin();
318}
319
320bool AnnexBBufferReader::SeekToNextNaluOfType(NaluType type) {
321 for (; offset_ != offsets_.end(); ++offset_) {
322 if (offset_->payload_size < 1)
323 continue;
324 if (ParseNaluType(*(start_ + offset_->payload_start_offset)) == type)
325 return true;
326 }
327 return false;
328}
Zeke Chin71f6f442015-06-29 14:34:58 -0700329AvccBufferWriter::AvccBufferWriter(uint8_t* const avcc_buffer, size_t length)
330 : start_(avcc_buffer), offset_(0), length_(length) {
henrikg91d6ede2015-09-17 00:24:34 -0700331 RTC_DCHECK(avcc_buffer);
Zeke Chin71f6f442015-06-29 14:34:58 -0700332}
333
334bool AvccBufferWriter::WriteNalu(const uint8_t* data, size_t data_size) {
335 // Check if we can write this length of data.
336 if (data_size + kAvccHeaderByteSize > BytesRemaining()) {
337 return false;
338 }
339 // Write length header, which needs to be big endian.
340 uint32_t big_endian_length = CFSwapInt32HostToBig(data_size);
341 memcpy(start_ + offset_, &big_endian_length, sizeof(big_endian_length));
342 offset_ += sizeof(big_endian_length);
343 // Write data.
344 memcpy(start_ + offset_, data, data_size);
345 offset_ += data_size;
346 return true;
347}
348
349size_t AvccBufferWriter::BytesRemaining() const {
350 return length_ - offset_;
351}
352
353} // namespace webrtc