OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/base/mac/videotoolbox_helpers.h" | 5 #include "media/base/mac/videotoolbox_helpers.h" |
6 | 6 |
7 #include <array> | 7 #include <array> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/big_endian.h" | 10 #include "base/big_endian.h" |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 avcc_buffer += sizeof(NalSizeType); | 128 avcc_buffer += sizeof(NalSizeType); |
129 | 129 |
130 DCHECK_GE(bytes_left, nal_size); | 130 DCHECK_GE(bytes_left, nal_size); |
131 annexb_buffer->Append(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes)); | 131 annexb_buffer->Append(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes)); |
132 annexb_buffer->Append(avcc_buffer, nal_size); | 132 annexb_buffer->Append(avcc_buffer, nal_size); |
133 bytes_left -= nal_size; | 133 bytes_left -= nal_size; |
134 avcc_buffer += nal_size; | 134 avcc_buffer += nal_size; |
135 } | 135 } |
136 } | 136 } |
137 | 137 |
138 bool CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf, | 138 bool CopySampleBufferToAnnexBBuffer(CMSampleBufferRef sbuf, |
139 AnnexBBuffer* annexb_buffer, | 139 AnnexBBuffer* annexb_buffer, |
140 bool keyframe) { | 140 bool keyframe) { |
141 // Perform two pass, one to figure out the total output size, and another to | 141 // Perform two pass, one to figure out the total output size, and another to |
142 // copy the data after having performed a single output allocation. Note that | 142 // copy the data after having performed a single output allocation. Note that |
143 // we'll allocate a bit more because we'll count 4 bytes instead of 3 for | 143 // we'll allocate a bit more because we'll count 4 bytes instead of 3 for |
144 // video NALs. | 144 // video NALs. |
145 OSStatus status; | 145 OSStatus status; |
146 | 146 |
147 // Get the sample buffer's block buffer and format description. | 147 // Get the sample buffer's block buffer and format description. |
148 auto* bb = CoreMediaGlue::CMSampleBufferGetDataBuffer(sbuf); | 148 auto* bb = CMSampleBufferGetDataBuffer(sbuf); |
149 DCHECK(bb); | 149 DCHECK(bb); |
150 auto* fdesc = CoreMediaGlue::CMSampleBufferGetFormatDescription(sbuf); | 150 auto* fdesc = CMSampleBufferGetFormatDescription(sbuf); |
151 DCHECK(fdesc); | 151 DCHECK(fdesc); |
152 | 152 |
153 size_t bb_size = CoreMediaGlue::CMBlockBufferGetDataLength(bb); | 153 size_t bb_size = CMBlockBufferGetDataLength(bb); |
154 size_t total_bytes = bb_size; | 154 size_t total_bytes = bb_size; |
155 | 155 |
156 size_t pset_count; | 156 size_t pset_count; |
157 int nal_size_field_bytes; | 157 int nal_size_field_bytes; |
158 status = CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | 158 status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex( |
159 fdesc, 0, nullptr, nullptr, &pset_count, &nal_size_field_bytes); | 159 fdesc, 0, nullptr, nullptr, &pset_count, &nal_size_field_bytes); |
160 if (status == | 160 if (status == kCMFormatDescriptionBridgeError_InvalidParameter) { |
161 CoreMediaGlue::kCMFormatDescriptionBridgeError_InvalidParameter) { | |
162 DLOG(WARNING) << " assuming 2 parameter sets and 4 bytes NAL length header"; | 161 DLOG(WARNING) << " assuming 2 parameter sets and 4 bytes NAL length header"; |
163 pset_count = 2; | 162 pset_count = 2; |
164 nal_size_field_bytes = 4; | 163 nal_size_field_bytes = 4; |
165 } else if (status != noErr) { | 164 } else if (status != noErr) { |
166 DLOG(ERROR) | 165 DLOG(ERROR) |
167 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | 166 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " |
168 << status; | 167 << status; |
169 return false; | 168 return false; |
170 } | 169 } |
171 | 170 |
172 if (keyframe) { | 171 if (keyframe) { |
173 const uint8_t* pset; | 172 const uint8_t* pset; |
174 size_t pset_size; | 173 size_t pset_size; |
175 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { | 174 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { |
176 status = | 175 status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex( |
177 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | 176 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); |
178 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); | |
179 if (status != noErr) { | 177 if (status != noErr) { |
180 DLOG(ERROR) | 178 DLOG(ERROR) |
181 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | 179 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " |
182 << status; | 180 << status; |
183 return false; | 181 return false; |
184 } | 182 } |
185 total_bytes += pset_size + nal_size_field_bytes; | 183 total_bytes += pset_size + nal_size_field_bytes; |
186 } | 184 } |
187 } | 185 } |
188 | 186 |
189 if (!annexb_buffer->Reserve(total_bytes)) { | 187 if (!annexb_buffer->Reserve(total_bytes)) { |
190 DLOG(ERROR) << "Cannot fit encode output into bitstream buffer. Requested:" | 188 DLOG(ERROR) << "Cannot fit encode output into bitstream buffer. Requested:" |
191 << total_bytes; | 189 << total_bytes; |
192 return false; | 190 return false; |
193 } | 191 } |
194 | 192 |
195 // Copy all parameter sets before keyframes. | 193 // Copy all parameter sets before keyframes. |
196 if (keyframe) { | 194 if (keyframe) { |
197 const uint8_t* pset; | 195 const uint8_t* pset; |
198 size_t pset_size; | 196 size_t pset_size; |
199 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { | 197 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { |
200 status = | 198 status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex( |
201 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | 199 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); |
202 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); | |
203 if (status != noErr) { | 200 if (status != noErr) { |
204 DLOG(ERROR) | 201 DLOG(ERROR) |
205 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | 202 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " |
206 << status; | 203 << status; |
207 return false; | 204 return false; |
208 } | 205 } |
209 annexb_buffer->Append(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes)); | 206 annexb_buffer->Append(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes)); |
210 annexb_buffer->Append(reinterpret_cast<const char*>(pset), pset_size); | 207 annexb_buffer->Append(reinterpret_cast<const char*>(pset), pset_size); |
211 } | 208 } |
212 } | 209 } |
213 | 210 |
214 // Block buffers can be composed of non-contiguous chunks. For the sake of | 211 // Block buffers can be composed of non-contiguous chunks. For the sake of |
215 // keeping this code simple, flatten non-contiguous block buffers. | 212 // keeping this code simple, flatten non-contiguous block buffers. |
216 base::ScopedCFTypeRef<CoreMediaGlue::CMBlockBufferRef> contiguous_bb( | 213 base::ScopedCFTypeRef<CMBlockBufferRef> contiguous_bb( |
217 bb, base::scoped_policy::RETAIN); | 214 bb, base::scoped_policy::RETAIN); |
218 if (!CoreMediaGlue::CMBlockBufferIsRangeContiguous(bb, 0, 0)) { | 215 if (!CMBlockBufferIsRangeContiguous(bb, 0, 0)) { |
219 contiguous_bb.reset(); | 216 contiguous_bb.reset(); |
220 status = CoreMediaGlue::CMBlockBufferCreateContiguous( | 217 status = CMBlockBufferCreateContiguous(kCFAllocatorDefault, bb, |
221 kCFAllocatorDefault, bb, kCFAllocatorDefault, nullptr, 0, 0, 0, | 218 kCFAllocatorDefault, nullptr, 0, 0, |
222 contiguous_bb.InitializeInto()); | 219 0, contiguous_bb.InitializeInto()); |
223 if (status != noErr) { | 220 if (status != noErr) { |
224 DLOG(ERROR) << " CMBlockBufferCreateContiguous failed: " << status; | 221 DLOG(ERROR) << " CMBlockBufferCreateContiguous failed: " << status; |
225 return false; | 222 return false; |
226 } | 223 } |
227 } | 224 } |
228 | 225 |
229 // Copy all the NAL units. In the process convert them from AVCC format | 226 // Copy all the NAL units. In the process convert them from AVCC format |
230 // (length header) to AnnexB format (start code). | 227 // (length header) to AnnexB format (start code). |
231 char* bb_data; | 228 char* bb_data; |
232 status = CoreMediaGlue::CMBlockBufferGetDataPointer(contiguous_bb, 0, nullptr, | 229 status = |
233 nullptr, &bb_data); | 230 CMBlockBufferGetDataPointer(contiguous_bb, 0, nullptr, nullptr, &bb_data); |
234 if (status != noErr) { | 231 if (status != noErr) { |
235 DLOG(ERROR) << " CMBlockBufferGetDataPointer failed: " << status; | 232 DLOG(ERROR) << " CMBlockBufferGetDataPointer failed: " << status; |
236 return false; | 233 return false; |
237 } | 234 } |
238 | 235 |
239 if (nal_size_field_bytes == 1) { | 236 if (nal_size_field_bytes == 1) { |
240 CopyNalsToAnnexB<uint8_t>(bb_data, bb_size, annexb_buffer); | 237 CopyNalsToAnnexB<uint8_t>(bb_data, bb_size, annexb_buffer); |
241 } else if (nal_size_field_bytes == 2) { | 238 } else if (nal_size_field_bytes == 2) { |
242 CopyNalsToAnnexB<uint16_t>(bb_data, bb_size, annexb_buffer); | 239 CopyNalsToAnnexB<uint16_t>(bb_data, bb_size, annexb_buffer); |
243 } else if (nal_size_field_bytes == 4) { | 240 } else if (nal_size_field_bytes == 4) { |
244 CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer); | 241 CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer); |
245 } else { | 242 } else { |
246 NOTREACHED(); | 243 NOTREACHED(); |
247 } | 244 } |
248 return true; | 245 return true; |
249 } | 246 } |
250 | 247 |
251 bool CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf, | 248 bool CopySampleBufferToAnnexBBuffer(CMSampleBufferRef sbuf, |
252 bool keyframe, | 249 bool keyframe, |
253 std::string* annexb_buffer) { | 250 std::string* annexb_buffer) { |
254 StringAnnexBBuffer buffer(annexb_buffer); | 251 StringAnnexBBuffer buffer(annexb_buffer); |
255 return CopySampleBufferToAnnexBBuffer(sbuf, &buffer, keyframe); | 252 return CopySampleBufferToAnnexBBuffer(sbuf, &buffer, keyframe); |
256 } | 253 } |
257 | 254 |
258 bool CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf, | 255 bool CopySampleBufferToAnnexBBuffer(CMSampleBufferRef sbuf, |
259 bool keyframe, | 256 bool keyframe, |
260 size_t annexb_buffer_size, | 257 size_t annexb_buffer_size, |
261 char* annexb_buffer, | 258 char* annexb_buffer, |
262 size_t* used_buffer_size) { | 259 size_t* used_buffer_size) { |
263 RawAnnexBBuffer buffer(annexb_buffer, annexb_buffer_size); | 260 RawAnnexBBuffer buffer(annexb_buffer, annexb_buffer_size); |
264 const bool copy_rv = CopySampleBufferToAnnexBBuffer(sbuf, &buffer, keyframe); | 261 const bool copy_rv = CopySampleBufferToAnnexBBuffer(sbuf, &buffer, keyframe); |
265 *used_buffer_size = buffer.GetReservedSize(); | 262 *used_buffer_size = buffer.GetReservedSize(); |
266 return copy_rv; | 263 return copy_rv; |
267 } | 264 } |
268 | 265 |
269 SessionPropertySetter::SessionPropertySetter( | 266 SessionPropertySetter::SessionPropertySetter( |
270 base::ScopedCFTypeRef<VideoToolboxGlue::VTCompressionSessionRef> session, | 267 base::ScopedCFTypeRef<VTCompressionSessionRef> session) |
271 const VideoToolboxGlue* const glue) | 268 : session_(session) {} |
272 : session_(session), glue_(glue) {} | |
273 | 269 |
274 SessionPropertySetter::~SessionPropertySetter() {} | 270 SessionPropertySetter::~SessionPropertySetter() {} |
275 | 271 |
276 bool SessionPropertySetter::Set(CFStringRef key, int32_t value) { | 272 bool SessionPropertySetter::Set(CFStringRef key, int32_t value) { |
277 DCHECK(session_); | 273 DCHECK(session_); |
278 DCHECK(glue_); | |
279 base::ScopedCFTypeRef<CFNumberRef> cfvalue( | 274 base::ScopedCFTypeRef<CFNumberRef> cfvalue( |
280 CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)); | 275 CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)); |
281 return glue_->VTSessionSetProperty(session_, key, cfvalue) == noErr; | 276 return VTSessionSetProperty(session_, key, cfvalue) == noErr; |
282 } | 277 } |
283 | 278 |
284 bool SessionPropertySetter::Set(CFStringRef key, bool value) { | 279 bool SessionPropertySetter::Set(CFStringRef key, bool value) { |
285 DCHECK(session_); | 280 DCHECK(session_); |
286 DCHECK(glue_); | |
287 CFBooleanRef cfvalue = (value) ? kCFBooleanTrue : kCFBooleanFalse; | 281 CFBooleanRef cfvalue = (value) ? kCFBooleanTrue : kCFBooleanFalse; |
288 return glue_->VTSessionSetProperty(session_, key, cfvalue) == noErr; | 282 return VTSessionSetProperty(session_, key, cfvalue) == noErr; |
289 } | 283 } |
290 | 284 |
291 bool SessionPropertySetter::Set(CFStringRef key, CFStringRef value) { | 285 bool SessionPropertySetter::Set(CFStringRef key, CFStringRef value) { |
292 DCHECK(session_); | 286 DCHECK(session_); |
293 DCHECK(glue_); | 287 return VTSessionSetProperty(session_, key, value) == noErr; |
294 return glue_->VTSessionSetProperty(session_, key, value) == noErr; | |
295 } | 288 } |
296 | 289 |
297 bool SessionPropertySetter::Set(CFStringRef key, CFArrayRef value) { | 290 bool SessionPropertySetter::Set(CFStringRef key, CFArrayRef value) { |
298 DCHECK(session_); | 291 DCHECK(session_); |
299 DCHECK(glue_); | 292 return VTSessionSetProperty(session_, key, value) == noErr; |
300 return glue_->VTSessionSetProperty(session_, key, value) == noErr; | |
301 } | 293 } |
302 | 294 |
303 } // namespace video_toolbox | 295 } // namespace video_toolbox |
304 | 296 |
305 } // namespace media | 297 } // namespace media |
OLD | NEW |