Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(331)

Side by Side Diff: media/base/mac/videotoolbox_helpers.cc

Issue 1636083003: H264 HW encode using VideoToolbox (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/base/mac/videotoolbox_helpers.h"
6
7 #include <vector>
8
9 #include "base/big_endian.h"
10
11 namespace media {
12
13 // Copy a H.264 frame stored in a CM sample buffer to an Annex B buffer. Copies
14 // parameter sets for keyframes before the frame data as well.
15 void CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf,
16 std::string* annexb_buffer,
17 bool keyframe) {
18 // Perform two pass, one to figure out the total output size, and another to
19 // copy the data after having performed a single output allocation. Note that
20 // we'll allocate a bit more because we'll count 4 bytes instead of 3 for
21 // video NALs.
22
23 OSStatus status;
24
25 // Get the sample buffer's block buffer and format description.
26 auto bb = CoreMediaGlue::CMSampleBufferGetDataBuffer(sbuf);
27 DCHECK(bb);
28 auto fdesc = CoreMediaGlue::CMSampleBufferGetFormatDescription(sbuf);
29 DCHECK(fdesc);
30
31 size_t bb_size = CoreMediaGlue::CMBlockBufferGetDataLength(bb);
32 size_t total_bytes = bb_size;
33
34 size_t pset_count;
35 int nal_size_field_bytes;
36 status = CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
37 fdesc, 0, nullptr, nullptr, &pset_count, &nal_size_field_bytes);
38 if (status ==
39 CoreMediaGlue::kCMFormatDescriptionBridgeError_InvalidParameter) {
40 DLOG(WARNING) << " assuming 2 parameter sets and 4 bytes NAL length header";
41 pset_count = 2;
42 nal_size_field_bytes = 4;
43 } else if (status != noErr) {
44 DLOG(ERROR)
45 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: "
46 << status;
47 return;
48 }
49
50 if (keyframe) {
51 const uint8_t* pset;
52 size_t pset_size;
53 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) {
54 status =
55 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
56 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr);
57 if (status != noErr) {
58 DLOG(ERROR)
59 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: "
60 << status;
61 return;
62 }
63 total_bytes += pset_size + nal_size_field_bytes;
64 }
65 }
66
67 annexb_buffer->reserve(total_bytes);
68
69 // Copy all parameter sets before keyframes.
70 if (keyframe) {
71 const uint8_t* pset;
72 size_t pset_size;
73 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) {
74 status =
75 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
76 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr);
77 if (status != noErr) {
78 DLOG(ERROR)
79 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: "
80 << status;
81 return;
82 }
83 static const char startcode_4[4] = {0, 0, 0, 1};
84 annexb_buffer->append(startcode_4, sizeof(startcode_4));
85 annexb_buffer->append(reinterpret_cast<const char*>(pset), pset_size);
86 }
87 }
88
89 // Block buffers can be composed of non-contiguous chunks. For the sake of
90 // keeping this code simple, flatten non-contiguous block buffers.
91 base::ScopedCFTypeRef<CoreMediaGlue::CMBlockBufferRef> contiguous_bb(
92 bb, base::scoped_policy::RETAIN);
93 if (!CoreMediaGlue::CMBlockBufferIsRangeContiguous(bb, 0, 0)) {
94 contiguous_bb.reset();
95 status = CoreMediaGlue::CMBlockBufferCreateContiguous(
96 kCFAllocatorDefault, bb, kCFAllocatorDefault, nullptr, 0, 0, 0,
97 contiguous_bb.InitializeInto());
98 if (status != noErr) {
99 DLOG(ERROR) << " CMBlockBufferCreateContiguous failed: " << status;
100 return;
101 }
102 }
103
104 // Copy all the NAL units. In the process convert them from AVCC format
105 // (length header) to AnnexB format (start code).
106 char* bb_data;
107 status = CoreMediaGlue::CMBlockBufferGetDataPointer(contiguous_bb, 0, nullptr,
108 nullptr, &bb_data);
109 if (status != noErr) {
110 DLOG(ERROR) << " CMBlockBufferGetDataPointer failed: " << status;
111 return;
112 }
113
114 if (nal_size_field_bytes == 1) {
115 CopyNalsToAnnexB<uint8_t>(bb_data, bb_size, annexb_buffer);
116 } else if (nal_size_field_bytes == 2) {
117 CopyNalsToAnnexB<uint16_t>(bb_data, bb_size, annexb_buffer);
118 } else if (nal_size_field_bytes == 4) {
119 CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer);
120 } else {
121 NOTREACHED();
122 }
123 }
124
125 template <typename NalSizeType>
126 void CopyNalsToAnnexB(char* avcc_buffer,
127 const size_t avcc_size,
128 std::string* annexb_buffer) {
129 static_assert(sizeof(NalSizeType) == 1 || sizeof(NalSizeType) == 2 ||
130 sizeof(NalSizeType) == 4,
131 "NAL size type has unsupported size");
132 static const char startcode_3[3] = {0, 0, 1};
133 DCHECK(avcc_buffer);
134 DCHECK(annexb_buffer);
135 size_t bytes_left = avcc_size;
136 while (bytes_left > 0) {
137 DCHECK_GT(bytes_left, sizeof(NalSizeType));
138 NalSizeType nal_size;
139 base::ReadBigEndian(avcc_buffer, &nal_size);
140 bytes_left -= sizeof(NalSizeType);
141 avcc_buffer += sizeof(NalSizeType);
142
143 DCHECK_GE(bytes_left, nal_size);
144 annexb_buffer->append(startcode_3, sizeof(startcode_3));
145 annexb_buffer->append(avcc_buffer, nal_size);
146 bytes_left -= nal_size;
147 avcc_buffer += nal_size;
148 }
149 }
150
151 base::ScopedCFTypeRef<CFDictionaryRef>
152 DictionaryWithKeysAndValues(CFTypeRef* keys, CFTypeRef* values, size_t size) {
153 return base::ScopedCFTypeRef<CFDictionaryRef>(CFDictionaryCreate(
154 kCFAllocatorDefault, keys, values, size, &kCFTypeDictionaryKeyCallBacks,
155 &kCFTypeDictionaryValueCallBacks));
156 }
157
158 base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeyValue(CFTypeRef key,
159 CFTypeRef value) {
160 CFTypeRef keys[1] = {key};
161 CFTypeRef values[1] = {value};
162 return DictionaryWithKeysAndValues(keys, values, 1);
163 }
164
165 base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegers(const int* v, size_t size) {
166 std::vector<CFNumberRef> numbers;
167 numbers.reserve(size);
168 for (const int* end = v + size; v < end; ++v)
169 numbers.push_back(CFNumberCreate(nullptr, kCFNumberSInt32Type, v));
170 base::ScopedCFTypeRef<CFArrayRef> array(CFArrayCreate(
171 kCFAllocatorDefault, reinterpret_cast<const void**>(&numbers[0]),
172 numbers.size(), &kCFTypeArrayCallBacks));
173 for (auto& number : numbers) {
174 CFRelease(number);
175 }
176 return array;
177 }
178
179 bool SetSessionProperty(base::ScopedCFTypeRef<VTCompressionSessionRef> session,
180 const VideoToolboxGlue* const glue,
181 CFStringRef key,
182 int32_t value) {
183 base::ScopedCFTypeRef<CFNumberRef> cfvalue(
184 CFNumberCreate(nullptr, kCFNumberSInt32Type, &value));
185 return glue->VTSessionSetProperty(session, key, cfvalue) == noErr;
186 }
187
188 bool SetSessionProperty(base::ScopedCFTypeRef<VTCompressionSessionRef> session,
189 const VideoToolboxGlue* const glue,
190 CFStringRef key,
191 bool value) {
192 CFBooleanRef cfvalue = (value) ? kCFBooleanTrue : kCFBooleanFalse;
193 return glue->VTSessionSetProperty(session, key, cfvalue) == noErr;
194 }
195
196 bool SetSessionProperty(base::ScopedCFTypeRef<VTCompressionSessionRef> session,
197 const VideoToolboxGlue* const glue,
198 CFStringRef key,
199 CFStringRef value) {
200 return glue->VTSessionSetProperty(session, key, value) == noErr;
201 }
202
203 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698