| 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 |