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

Side by Side Diff: content/common/gpu/media/mac_video_decode_accelerator.mm

Issue 10388108: Implement media::VideoDecodeAccelerator on Mac (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: a Created 8 years, 6 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "content/common/gpu/media/mac_video_decode_accelerator.h"
6
7 #include "base/bind.h"
8 #include "base/file_path.h"
9 #import "base/mac/foundation_util.h"
10 #import "base/memory/ref_counted_memory.h"
11 #import "base/message_loop.h"
12 #include "base/location.h"
13 #include "base/native_library.h"
14 #include "ui/surface/io_surface_support_mac.h"
15 #include "ui/gfx/video_decode_acceleration_support_mac.h"
16
17 // Helper macros for dealing with failure. If |result| evaluates false, emit
18 // |log| to ERROR, register |error| with the decoder, and return |ret_val|
19 // (which may be omitted for functions that return void).
20 #define RETURN_ON_FAILURE(result, log, error, ret_val) \
21 do { \
22 if (!(result)) { \
23 DLOG(ERROR) << log; \
24 StopOnError(error); \
25 return ret_val; \
26 } \
27 } while (0)
28
29 namespace {
30
31 enum { kNumPictureBuffers = 4 };
32
33 class ScopedContextSetter {
34 public:
35 ScopedContextSetter(CGLContextObj context)
36 : old_context_(NULL),
37 did_succeed_(false) {
38 old_context_ = CGLGetCurrentContext();
39 did_succeed_ = CGLSetCurrentContext(context) == kCGLNoError;
40 }
41
42 ~ScopedContextSetter() {
43 if (did_succeed_)
44 CGLSetCurrentContext(old_context_);
45 }
46
47 bool did_succeed() const {
48 return did_succeed_;
49 }
50
51 private:
52 CGLContextObj old_context_;
53 bool did_succeed_;
54 };
55
56 } // namespace
57
58 static bool BindImageToTexture(CGLContextObj context,
59 CVImageBufferRef image,
60 uint32 texture_id) {
61 ScopedContextSetter scoped_context_setter(context);
62 if (!scoped_context_setter.did_succeed()) {
63 DVLOG(1) << "Unable to set OpenGL context.";
64 return false;
65 }
66
67 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
68 DCHECK(io_surface_support);
69
70 CFTypeRef io_surface =
71 io_surface_support->CVPixelBufferGetIOSurface(image);
72 if (!io_surface) {
73 DVLOG(1) << "Unable to get IOSurface for CVPixelBuffer.";
74 return false;
75 }
76
77 glEnable(GL_TEXTURE_RECTANGLE_ARB);
78 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id);
79 if (io_surface_support->CGLTexImageIOSurface2D(
80 context,
81 GL_TEXTURE_RECTANGLE_ARB,
82 GL_RGB,
83 io_surface_support->IOSurfaceGetWidth(io_surface),
84 io_surface_support->IOSurfaceGetHeight(io_surface),
85 GL_YCBCR_422_APPLE,
86 GL_UNSIGNED_SHORT_8_8_APPLE,
87 io_surface,
88 0) != kCGLNoError) {
89 DVLOG(1) << "Failed to bind image to texture.";
90 return false;
91 }
92 return glGetError() == GL_NO_ERROR;
93 }
94
95 MacVideoDecodeAccelerator::MacVideoDecodeAccelerator(
96 media::VideoDecodeAccelerator::Client* client)
97 : client_(client),
98 cgl_context_(NULL),
99 nalu_len_field_size_(0),
100 did_request_pictures_(false) {
101 }
102
103 void MacVideoDecodeAccelerator::SetCGLContext(CGLContextObj cgl_context) {
104 DCHECK(CalledOnValidThread());
105 cgl_context_ = cgl_context;
106 }
107
108 bool MacVideoDecodeAccelerator::SetConfigInfo(
109 uint32_t frame_width,
110 uint32_t frame_height,
111 const std::vector<uint8_t>& avc_data) {
112 DCHECK(CalledOnValidThread());
113 frame_size_ = gfx::Size(frame_width, frame_height);
114 nalu_len_field_size_ = (avc_data[4] & 0x03) + 1;
115
116 DCHECK(!vda_support_.get());
117 vda_support_ = new gfx::VideoDecodeAccelerationSupport();
118 gfx::VideoDecodeAccelerationSupport::Status status = vda_support_->Create(
119 frame_size_.width(), frame_size_.height(), kCVPixelFormatType_422YpCbCr8,
120 &avc_data.front(), avc_data.size());
121 RETURN_ON_FAILURE(status == gfx::VideoDecodeAccelerationSupport::SUCCESS,
122 "Creating video decoder failed with error: " << status,
123 PLATFORM_FAILURE, false);
124 return true;
125 }
126
127 bool MacVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile) {
128 DCHECK(CalledOnValidThread());
129
130 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
131 if (!io_surface_support)
132 return false;
133
134 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
135 &MacVideoDecodeAccelerator::NotifyInitializeDone, this));
136 return true;
137 }
138
139 void MacVideoDecodeAccelerator::Decode(
140 const media::BitstreamBuffer& bitstream_buffer) {
141 DCHECK(CalledOnValidThread());
142 RETURN_ON_FAILURE(client_,
143 "Call to Decode() during invalid state.", ILLEGAL_STATE,);
144
145 base::SharedMemory memory(bitstream_buffer.handle(), true);
146 RETURN_ON_FAILURE(memory.Map(bitstream_buffer.size()),
147 "Failed to SharedMemory::Map().", UNREADABLE_INPUT,);
148
149 size_t buffer_size = bitstream_buffer.size();
150 RETURN_ON_FAILURE(buffer_size > nalu_len_field_size_,
151 "Bitstream contains invalid data.", INVALID_ARGUMENT,);
152
153 // The decoder can only handle slice types 1-5.
154 const uint8_t* buffer = static_cast<const uint8_t*>(memory.memory());
155 uint8_t nalu_type = buffer[nalu_len_field_size_] & 0x1f;
156 if (nalu_type < 1 || nalu_type > 5) {
157 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
158 &MacVideoDecodeAccelerator::NotifyInputBufferRead, this,
159 bitstream_buffer.id()));
160 return;
161 }
162
163 // Keep a ref counted copy of the buffer.
164 std::vector<uint8_t> vbuffer(buffer, buffer + buffer_size);
165 scoped_refptr<base::RefCountedBytes> bytes(
166 base::RefCountedBytes::TakeVector(&vbuffer));
167
168 // Store the buffer size at the beginning of the buffer as the decoder
169 // expects.
170 size_t frame_buffer_size = buffer_size - nalu_len_field_size_;
171 const uint64_t max_frame_buffer_size =
172 (1llu << (nalu_len_field_size_ * 8)) - 1;
173 DCHECK_LE(nalu_len_field_size_, 4u);
174 RETURN_ON_FAILURE(frame_buffer_size <= max_frame_buffer_size,
175 "Bitstream buffer is too large.", INVALID_ARGUMENT,);
Ami GONE FROM CHROMIUM 2012/05/30 00:29:58 double space
sail 2012/05/30 20:13:46 Done.
176 for (size_t i = 0; i < nalu_len_field_size_; ++i) {
177 size_t shift = nalu_len_field_size_ * 8 - (i + 1) * 8;
178 bytes->data()[i] = (frame_buffer_size >> shift) & 0xff;
179 }
180
181 vda_support_->Decode(bytes->front(), bytes->size(),
182 base::Bind(&MacVideoDecodeAccelerator::OnFrameReady,
183 this, bitstream_buffer.id(), bytes));
184
185 if (!did_request_pictures_) {
186 did_request_pictures_ = true;
187 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
188 &MacVideoDecodeAccelerator::RequestPictures, this));
189 }
190 }
191
192 void MacVideoDecodeAccelerator::AssignPictureBuffers(
193 const std::vector<media::PictureBuffer>& buffers) {
194 DCHECK(CalledOnValidThread());
195 RETURN_ON_FAILURE(client_,
196 "Call to AssignPictureBuffers() during invalid state.",
197 ILLEGAL_STATE,);
198 available_pictures_.insert(
199 available_pictures_.end(), buffers.begin(), buffers.end());
200 SendImages();
201 }
202
203 void MacVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
204 DCHECK(CalledOnValidThread());
205 RETURN_ON_FAILURE(client_,
206 "Call to ReusePictureBuffe() during invalid state.",
207 ILLEGAL_STATE,);
208
209 std::map<int32, UsedPictureInfo>::iterator it =
210 used_pictures_.find(picture_buffer_id);
211 RETURN_ON_FAILURE(it != used_pictures_.end(),
212 "Missing picture buffer id: " << picture_buffer_id,
213 INVALID_ARGUMENT,);
214 UsedPictureInfo info = it->second;
215 used_pictures_.erase(it);
216 available_pictures_.push_back(info.picture_buffer);
217 SendImages();
218 }
219
220 void MacVideoDecodeAccelerator::Flush() {
221 DCHECK(CalledOnValidThread());
222 RETURN_ON_FAILURE(client_,
223 "Call to Flush() during invalid state.", ILLEGAL_STATE,);
224 vda_support_->Flush(true);
225 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
226 &MacVideoDecodeAccelerator::NotifyFlushDone, this));
227 }
228
229 void MacVideoDecodeAccelerator::Reset() {
230 DCHECK(CalledOnValidThread());
231 RETURN_ON_FAILURE(client_,
232 "Call to Reset() during invalid state.", ILLEGAL_STATE,);
233 vda_support_->Flush(false);
234 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
235 &MacVideoDecodeAccelerator::NotifyResetDone, this));
236 }
237
238 void MacVideoDecodeAccelerator::Destroy() {
239 DCHECK(CalledOnValidThread());
240 if (vda_support_) {
241 vda_support_->Destroy();
242 vda_support_ = NULL;
243 }
244 client_ = NULL;
245 decoded_images_.clear();
246 }
247
248 MacVideoDecodeAccelerator::~MacVideoDecodeAccelerator() {
249 DCHECK(CalledOnValidThread());
250 Destroy();
251 }
252
253 void MacVideoDecodeAccelerator::OnFrameReady(
254 int32 bitstream_buffer_id,
255 scoped_refptr<base::RefCountedBytes> bytes,
256 CVImageBufferRef image,
257 int status) {
258 DCHECK(CalledOnValidThread());
259 RETURN_ON_FAILURE(status == noErr,
260 "Decoding image failed with error code: " << status,
261 PLATFORM_FAILURE,);
262 if (!client_)
263 return;
264 if (image) {
265 DecodedImageInfo info;
266 info.image.reset(image, base::mac::RETAIN);
267 info.bitstream_buffer_id = bitstream_buffer_id;
268 decoded_images_.push_back(info);
269 SendImages();
270 }
271 // TODO(sail): this assumes Decode() is handed a single NALU at a time. Make
272 // that assumption go away.
273 client_->NotifyEndOfBitstreamBuffer(bitstream_buffer_id);
274 }
275
276 void MacVideoDecodeAccelerator::SendImages() {
277 if (!client_) {
278 DCHECK(decoded_images_.empty());
279 return;
280 }
281
282 while (available_pictures_.size() && decoded_images_.size()) {
283 DecodedImageInfo info = decoded_images_.front();
284 decoded_images_.pop_front();
285 media::PictureBuffer picture_buffer = available_pictures_.front();
286 available_pictures_.pop_front();
287
288 RETURN_ON_FAILURE(BindImageToTexture(
289 cgl_context_, info.image, picture_buffer.texture_id()),
290 "Error binding image to texture.", PLATFORM_FAILURE,);
291
292 used_pictures_.insert(std::make_pair(
293 picture_buffer.id(), UsedPictureInfo(picture_buffer, info.image)));
294 client_->PictureReady(
295 media::Picture(picture_buffer.id(), info.bitstream_buffer_id));
296 }
297 }
298
299 void MacVideoDecodeAccelerator::StopOnError(
300 media::VideoDecodeAccelerator::Error error) {
301 if (client_)
302 client_->NotifyError(error);
303 Destroy();
304 }
305
306 void MacVideoDecodeAccelerator::NotifyInitializeDone() {
307 if (client_)
308 client_->NotifyInitializeDone();
309 }
310
311 void MacVideoDecodeAccelerator::RequestPictures() {
312 if (client_)
313 client_->ProvidePictureBuffers(kNumPictureBuffers, frame_size_);
314 }
315
316 void MacVideoDecodeAccelerator::NotifyFlushDone() {
317 if (client_)
318 client_->NotifyFlushDone();
319 }
320
321 void MacVideoDecodeAccelerator::NotifyResetDone() {
322 decoded_images_.clear();
323 if (client_)
324 client_->NotifyResetDone();
325 }
326
327 void MacVideoDecodeAccelerator::NotifyInputBufferRead(int input_buffer_id) {
328 if (client_)
329 client_->NotifyEndOfBitstreamBuffer(input_buffer_id);
330 }
331
332 MacVideoDecodeAccelerator::UsedPictureInfo::UsedPictureInfo(
333 const media::PictureBuffer& pic,
334 const base::mac::ScopedCFTypeRef<CVImageBufferRef>& image)
335 : picture_buffer(pic),
336 image(image, base::mac::RETAIN) {
337 }
338
339 MacVideoDecodeAccelerator::UsedPictureInfo::~UsedPictureInfo() {
340 }
341
342 MacVideoDecodeAccelerator::DecodedImageInfo::DecodedImageInfo() {
343 }
344
345 MacVideoDecodeAccelerator::DecodedImageInfo::~DecodedImageInfo() {
346 }
OLDNEW
« no previous file with comments | « content/common/gpu/media/mac_video_decode_accelerator.h ('k') | content/common/gpu/media/rendering_helper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698