OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/video_frame.h" | 5 #include "media/base/video_frame.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/aligned_memory.h" | 12 #include "base/memory/aligned_memory.h" |
13 #include "base/strings/string_piece.h" | 13 #include "base/strings/string_piece.h" |
14 #include "gpu/command_buffer/common/mailbox_holder.h" | 14 #include "gpu/command_buffer/common/mailbox_holder.h" |
15 #include "media/base/limits.h" | 15 #include "media/base/limits.h" |
16 #include "media/base/video_util.h" | 16 #include "media/base/video_util.h" |
17 #include "ui/gfx/point.h" | |
17 | 18 |
18 #if !defined(MEDIA_FOR_CAST_IOS) | 19 #if !defined(MEDIA_FOR_CAST_IOS) |
19 #include "third_party/skia/include/core/SkBitmap.h" | 20 #include "third_party/skia/include/core/SkBitmap.h" |
20 #endif | 21 #endif |
21 | 22 |
22 namespace media { | 23 namespace media { |
23 | 24 |
25 static bool IsPowerOfTwo(size_t x) { | |
26 return x != 0 && (x & (x - 1)) == 0; | |
27 } | |
28 | |
24 static inline size_t RoundUp(size_t value, size_t alignment) { | 29 static inline size_t RoundUp(size_t value, size_t alignment) { |
25 // Check that |alignment| is a power of 2. | 30 DCHECK(IsPowerOfTwo(alignment)); |
26 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); | |
27 return ((value + (alignment - 1)) & ~(alignment - 1)); | 31 return ((value + (alignment - 1)) & ~(alignment - 1)); |
28 } | 32 } |
29 | 33 |
34 static inline size_t RoundDown(size_t value, size_t alignment) { | |
35 DCHECK(IsPowerOfTwo(alignment)); | |
36 return value & ~(alignment - 1); | |
37 } | |
38 | |
39 // Returns the pixel size per element for given |plane| and |format|. E.g. 2x2 | |
40 // for the U-plane in I420. | |
41 static gfx::Size SampleSize(VideoFrame::Format format, size_t plane) { | |
42 DCHECK(VideoFrame::IsValidPlane(plane, format)); | |
43 | |
44 switch (plane) { | |
45 case VideoFrame::kYPlane: | |
46 case VideoFrame::kAPlane: | |
47 return gfx::Size(1, 1); | |
48 | |
49 case VideoFrame::kUPlane: // and VideoFrame::kUVPlane: | |
50 case VideoFrame::kVPlane: | |
51 switch (format) { | |
52 case VideoFrame::YV24: | |
53 return gfx::Size(1, 1); | |
54 | |
55 case VideoFrame::YV16: | |
56 return gfx::Size(2, 1); | |
57 | |
58 case VideoFrame::YV12: | |
59 case VideoFrame::YV12J: | |
60 case VideoFrame::I420: | |
61 case VideoFrame::YV12A: | |
62 case VideoFrame::NV12: | |
63 return gfx::Size(2, 2); | |
64 | |
65 case VideoFrame::UNKNOWN: | |
66 #if defined(VIDEO_HOLE) | |
67 case VideoFrame::HOLE: | |
68 #endif // defined(VIDEO_HOLE) | |
69 case VideoFrame::NATIVE_TEXTURE: | |
70 break; | |
71 } | |
72 } | |
73 NOTREACHED(); | |
74 return gfx::Size(); | |
75 } | |
76 | |
77 // Return the alignment for the whole frame, calculated as the max of the | |
78 // alignment for each individual plane. | |
79 static gfx::Size CommonAlignment(VideoFrame::Format format) { | |
80 int max_sample_width = 0; | |
81 int max_sample_height = 0; | |
82 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); ++plane) { | |
83 const gfx::Size sample_size = SampleSize(format, plane); | |
84 max_sample_width = std::max(max_sample_width, sample_size.width()); | |
85 max_sample_height = std::max(max_sample_height, sample_size.height()); | |
86 } | |
87 return gfx::Size(max_sample_width, max_sample_height); | |
88 } | |
89 | |
90 // Returns the number of bytes per element for given |plane| and |format|. E.g. | |
91 // 2 for the UV plane in NV12. | |
92 static int BytesPerElement(VideoFrame::Format format, size_t plane) { | |
93 DCHECK(VideoFrame::IsValidPlane(plane, format)); | |
94 return (format == VideoFrame::NV12 && plane == VideoFrame::kUVPlane) ? 2 : 1; | |
95 } | |
96 | |
30 // Rounds up |coded_size| if necessary for |format|. | 97 // Rounds up |coded_size| if necessary for |format|. |
31 static gfx::Size AdjustCodedSize(VideoFrame::Format format, | 98 static gfx::Size AdjustCodedSize(VideoFrame::Format format, |
32 const gfx::Size& coded_size) { | 99 const gfx::Size& coded_size) { |
33 gfx::Size new_coded_size(coded_size); | 100 const gfx::Size alignment = CommonAlignment(format); |
34 switch (format) { | 101 return gfx::Size(RoundUp(coded_size.width(), alignment.width()), |
35 case VideoFrame::YV12: | 102 RoundUp(coded_size.height(), alignment.height())); |
36 case VideoFrame::YV12A: | |
37 case VideoFrame::I420: | |
38 case VideoFrame::YV12J: | |
39 new_coded_size.set_height(RoundUp(new_coded_size.height(), 2)); | |
40 // Fallthrough. | |
41 case VideoFrame::YV16: | |
42 new_coded_size.set_width(RoundUp(new_coded_size.width(), 2)); | |
43 break; | |
44 default: | |
45 break; | |
46 } | |
47 return new_coded_size; | |
48 } | 103 } |
49 | 104 |
50 // static | 105 // static |
51 scoped_refptr<VideoFrame> VideoFrame::CreateFrame( | 106 scoped_refptr<VideoFrame> VideoFrame::CreateFrame( |
52 VideoFrame::Format format, | 107 VideoFrame::Format format, |
53 const gfx::Size& coded_size, | 108 const gfx::Size& coded_size, |
54 const gfx::Rect& visible_rect, | 109 const gfx::Rect& visible_rect, |
55 const gfx::Size& natural_size, | 110 const gfx::Size& natural_size, |
56 base::TimeDelta timestamp) { | 111 base::TimeDelta timestamp) { |
57 DCHECK(format != VideoFrame::UNKNOWN && | 112 DCHECK(format != VideoFrame::UNKNOWN && |
58 format != VideoFrame::NV12 && | 113 format != VideoFrame::NV12 && |
59 format != VideoFrame::NATIVE_TEXTURE); | 114 format != VideoFrame::NATIVE_TEXTURE); |
60 #if defined(VIDEO_HOLE) | 115 #if defined(VIDEO_HOLE) |
61 DCHECK(format != VideoFrame::HOLE); | 116 DCHECK(format != VideoFrame::HOLE); |
62 #endif // defined(VIDEO_HOLE) | 117 #endif // defined(VIDEO_HOLE) |
63 | 118 |
64 // Since we're creating a new YUV frame (and allocating memory for it | 119 // Since we're creating a new YUV frame (and allocating memory for it |
65 // ourselves), we can pad the requested |coded_size| if necessary if the | 120 // ourselves), we can pad the requested |coded_size| if necessary if the |
66 // request does not line up on sample boundaries. | 121 // request does not line up on sample boundaries. |
67 gfx::Size new_coded_size = AdjustCodedSize(format, coded_size); | 122 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size); |
scherkus (not reviewing)
2014/10/22 23:01:49
general tip for the future: try to avoid making th
magjed_chromium
2014/10/23 11:44:30
Yeah, sorry about that. I will keep that in mind i
| |
68 DCHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size)); | 123 DCHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size)); |
69 | 124 |
70 scoped_refptr<VideoFrame> frame( | 125 scoped_refptr<VideoFrame> frame( |
71 new VideoFrame(format, | 126 new VideoFrame(format, |
72 new_coded_size, | 127 new_coded_size, |
73 visible_rect, | 128 visible_rect, |
74 natural_size, | 129 natural_size, |
75 scoped_ptr<gpu::MailboxHolder>(), | 130 scoped_ptr<gpu::MailboxHolder>(), |
76 timestamp, | 131 timestamp, |
77 false)); | 132 false)); |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 natural_size.GetArea() > limits::kMaxCanvas || | 179 natural_size.GetArea() > limits::kMaxCanvas || |
125 natural_size.width() > limits::kMaxDimension || | 180 natural_size.width() > limits::kMaxDimension || |
126 natural_size.height() > limits::kMaxDimension) | 181 natural_size.height() > limits::kMaxDimension) |
127 return false; | 182 return false; |
128 | 183 |
129 // Check format-specific width/height requirements. | 184 // Check format-specific width/height requirements. |
130 switch (format) { | 185 switch (format) { |
131 case VideoFrame::UNKNOWN: | 186 case VideoFrame::UNKNOWN: |
132 return (coded_size.IsEmpty() && visible_rect.IsEmpty() && | 187 return (coded_size.IsEmpty() && visible_rect.IsEmpty() && |
133 natural_size.IsEmpty()); | 188 natural_size.IsEmpty()); |
189 | |
190 // NATIVE_TEXTURE and HOLE have no software-allocated buffers and are | |
191 // allowed to skip the below check. | |
192 case VideoFrame::NATIVE_TEXTURE: | |
193 #if defined(VIDEO_HOLE) | |
194 case VideoFrame::HOLE: | |
195 #endif // defined(VIDEO_HOLE) | |
196 return true; | |
197 | |
134 case VideoFrame::YV24: | 198 case VideoFrame::YV24: |
135 break; | |
136 case VideoFrame::YV12: | 199 case VideoFrame::YV12: |
137 case VideoFrame::YV12J: | 200 case VideoFrame::YV12J: |
138 case VideoFrame::I420: | 201 case VideoFrame::I420: |
139 case VideoFrame::YV12A: | 202 case VideoFrame::YV12A: |
140 case VideoFrame::NV12: | 203 case VideoFrame::NV12: |
141 // Subsampled YUV formats have width/height requirements. | |
142 if (static_cast<size_t>(coded_size.height()) < | |
143 RoundUp(visible_rect.bottom(), 2)) | |
144 return false; | |
145 // Fallthrough. | |
146 case VideoFrame::YV16: | 204 case VideoFrame::YV16: |
147 if (static_cast<size_t>(coded_size.width()) < | 205 // Check that software-allocated buffer formats are aligned correctly and |
148 RoundUp(visible_rect.right(), 2)) | 206 // not empty. |
149 return false; | 207 const gfx::Size alignment = CommonAlignment(format); |
150 break; | 208 return RoundUp(visible_rect.right(), alignment.width()) <= |
151 case VideoFrame::NATIVE_TEXTURE: | 209 static_cast<size_t>(coded_size.width()) && |
152 #if defined(VIDEO_HOLE) | 210 RoundUp(visible_rect.bottom(), alignment.height()) <= |
153 case VideoFrame::HOLE: | 211 static_cast<size_t>(coded_size.height()) && |
154 #endif // defined(VIDEO_HOLE) | 212 !coded_size.IsEmpty() && !visible_rect.IsEmpty() && |
155 // NATIVE_TEXTURE and HOLE have no software-allocated buffers and are | 213 !natural_size.IsEmpty(); |
156 // allowed to skip the below check and be empty. | |
157 return true; | |
158 } | 214 } |
159 | 215 |
160 // Check that software-allocated buffer formats are not empty. | 216 NOTREACHED(); |
161 return (!coded_size.IsEmpty() && !visible_rect.IsEmpty() && | 217 return false; |
162 !natural_size.IsEmpty()); | |
163 } | 218 } |
164 | 219 |
165 // static | 220 // static |
166 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture( | 221 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture( |
167 scoped_ptr<gpu::MailboxHolder> mailbox_holder, | 222 scoped_ptr<gpu::MailboxHolder> mailbox_holder, |
168 const ReleaseMailboxCB& mailbox_holder_release_cb, | 223 const ReleaseMailboxCB& mailbox_holder_release_cb, |
169 const gfx::Size& coded_size, | 224 const gfx::Size& coded_size, |
170 const gfx::Rect& visible_rect, | 225 const gfx::Rect& visible_rect, |
171 const gfx::Size& natural_size, | 226 const gfx::Size& natural_size, |
172 base::TimeDelta timestamp, | 227 base::TimeDelta timestamp, |
(...skipping 23 matching lines...) Expand all Loading... | |
196 scoped_refptr<VideoFrame> VideoFrame::WrapExternalPackedMemory( | 251 scoped_refptr<VideoFrame> VideoFrame::WrapExternalPackedMemory( |
197 Format format, | 252 Format format, |
198 const gfx::Size& coded_size, | 253 const gfx::Size& coded_size, |
199 const gfx::Rect& visible_rect, | 254 const gfx::Rect& visible_rect, |
200 const gfx::Size& natural_size, | 255 const gfx::Size& natural_size, |
201 uint8* data, | 256 uint8* data, |
202 size_t data_size, | 257 size_t data_size, |
203 base::SharedMemoryHandle handle, | 258 base::SharedMemoryHandle handle, |
204 base::TimeDelta timestamp, | 259 base::TimeDelta timestamp, |
205 const base::Closure& no_longer_needed_cb) { | 260 const base::Closure& no_longer_needed_cb) { |
206 gfx::Size new_coded_size = AdjustCodedSize(format, coded_size); | 261 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size); |
207 | 262 |
208 if (!IsValidConfig(format, new_coded_size, visible_rect, natural_size)) | 263 if (!IsValidConfig(format, new_coded_size, visible_rect, natural_size)) |
209 return NULL; | 264 return NULL; |
210 if (data_size < AllocationSize(format, new_coded_size)) | 265 if (data_size < AllocationSize(format, new_coded_size)) |
211 return NULL; | 266 return NULL; |
212 | 267 |
213 switch (format) { | 268 switch (format) { |
214 case VideoFrame::I420: { | 269 case VideoFrame::I420: { |
215 scoped_refptr<VideoFrame> frame( | 270 scoped_refptr<VideoFrame> frame( |
216 new VideoFrame(format, | 271 new VideoFrame(format, |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
284 #endif | 339 #endif |
285 | 340 |
286 #if defined(OS_MACOSX) | 341 #if defined(OS_MACOSX) |
287 // static | 342 // static |
288 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer( | 343 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer( |
289 CVPixelBufferRef cv_pixel_buffer, | 344 CVPixelBufferRef cv_pixel_buffer, |
290 base::TimeDelta timestamp) { | 345 base::TimeDelta timestamp) { |
291 DCHECK(cv_pixel_buffer); | 346 DCHECK(cv_pixel_buffer); |
292 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID()); | 347 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID()); |
293 | 348 |
294 OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer); | 349 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer); |
295 Format format; | 350 Format format; |
296 // There are very few compatible CV pixel formats, so just check each. | 351 // There are very few compatible CV pixel formats, so just check each. |
297 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) { | 352 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) { |
298 format = Format::I420; | 353 format = Format::I420; |
299 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) { | 354 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) { |
300 format = Format::YV24; | 355 format = Format::YV24; |
301 } else if (cv_format == '420v') { | 356 } else if (cv_format == '420v') { |
302 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the | 357 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the |
303 // minimum OS X and iOS SDKs permits it. | 358 // minimum OS X and iOS SDKs permits it. |
304 format = Format::NV12; | 359 format = Format::NV12; |
305 } else { | 360 } else { |
306 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format; | 361 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format; |
307 return NULL; | 362 return NULL; |
308 } | 363 } |
309 | 364 |
310 gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer)); | 365 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer)); |
311 gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer)); | 366 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer)); |
312 gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer)); | 367 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer)); |
313 | 368 |
314 if (!IsValidConfig(format, coded_size, visible_rect, natural_size)) | 369 if (!IsValidConfig(format, coded_size, visible_rect, natural_size)) |
315 return NULL; | 370 return NULL; |
316 | 371 |
317 scoped_refptr<VideoFrame> frame( | 372 scoped_refptr<VideoFrame> frame( |
318 new VideoFrame(format, | 373 new VideoFrame(format, |
319 coded_size, | 374 coded_size, |
320 visible_rect, | 375 visible_rect, |
321 natural_size, | 376 natural_size, |
322 scoped_ptr<gpu::MailboxHolder>(), | 377 scoped_ptr<gpu::MailboxHolder>(), |
(...skipping 12 matching lines...) Expand all Loading... | |
335 const gfx::Rect& visible_rect, | 390 const gfx::Rect& visible_rect, |
336 const gfx::Size& natural_size, | 391 const gfx::Size& natural_size, |
337 int32 y_stride, | 392 int32 y_stride, |
338 int32 u_stride, | 393 int32 u_stride, |
339 int32 v_stride, | 394 int32 v_stride, |
340 uint8* y_data, | 395 uint8* y_data, |
341 uint8* u_data, | 396 uint8* u_data, |
342 uint8* v_data, | 397 uint8* v_data, |
343 base::TimeDelta timestamp, | 398 base::TimeDelta timestamp, |
344 const base::Closure& no_longer_needed_cb) { | 399 const base::Closure& no_longer_needed_cb) { |
345 gfx::Size new_coded_size = AdjustCodedSize(format, coded_size); | 400 const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size); |
346 CHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size)); | 401 CHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size)); |
347 | 402 |
348 scoped_refptr<VideoFrame> frame( | 403 scoped_refptr<VideoFrame> frame( |
349 new VideoFrame(format, | 404 new VideoFrame(format, |
350 new_coded_size, | 405 new_coded_size, |
351 visible_rect, | 406 visible_rect, |
352 natural_size, | 407 natural_size, |
353 scoped_ptr<gpu::MailboxHolder>(), | 408 scoped_ptr<gpu::MailboxHolder>(), |
354 timestamp, | 409 timestamp, |
355 false)); | 410 false)); |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
490 size_t total = 0; | 545 size_t total = 0; |
491 for (size_t i = 0; i < NumPlanes(format); ++i) | 546 for (size_t i = 0; i < NumPlanes(format); ++i) |
492 total += PlaneAllocationSize(format, i, coded_size); | 547 total += PlaneAllocationSize(format, i, coded_size); |
493 return total; | 548 return total; |
494 } | 549 } |
495 | 550 |
496 // static | 551 // static |
497 gfx::Size VideoFrame::PlaneSize(Format format, | 552 gfx::Size VideoFrame::PlaneSize(Format format, |
498 size_t plane, | 553 size_t plane, |
499 const gfx::Size& coded_size) { | 554 const gfx::Size& coded_size) { |
555 DCHECK(IsValidPlane(plane, format)); | |
556 | |
500 // Align to multiple-of-two size overall. This ensures that non-subsampled | 557 // Align to multiple-of-two size overall. This ensures that non-subsampled |
501 // planes can be addressed by pixel with the same scaling as the subsampled | 558 // planes can be addressed by pixel with the same scaling as the subsampled |
502 // planes. | 559 // planes. |
503 const int width = RoundUp(coded_size.width(), 2); | 560 const int width = RoundUp(coded_size.width(), 2); |
504 const int height = RoundUp(coded_size.height(), 2); | 561 const int height = RoundUp(coded_size.height(), 2); |
505 switch (format) { | 562 |
506 case VideoFrame::YV24: | 563 const gfx::Size subsample = SampleSize(format, plane); |
507 switch (plane) { | 564 DCHECK(width % subsample.width() == 0 && height % subsample.height() == 0); |
scherkus (not reviewing)
2014/10/22 23:01:49
if this fails we won't know which condition failed
magjed_chromium
2014/10/23 11:44:31
Done.
| |
508 case VideoFrame::kYPlane: | 565 return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(), |
509 case VideoFrame::kUPlane: | 566 height / subsample.height()); |
510 case VideoFrame::kVPlane: | |
511 return gfx::Size(width, height); | |
512 default: | |
513 break; | |
514 } | |
515 break; | |
516 case VideoFrame::YV12: | |
517 case VideoFrame::YV12J: | |
518 case VideoFrame::I420: | |
519 switch (plane) { | |
520 case VideoFrame::kYPlane: | |
521 return gfx::Size(width, height); | |
522 case VideoFrame::kUPlane: | |
523 case VideoFrame::kVPlane: | |
524 return gfx::Size(width / 2, height / 2); | |
525 default: | |
526 break; | |
527 } | |
528 break; | |
529 case VideoFrame::YV12A: | |
530 switch (plane) { | |
531 case VideoFrame::kYPlane: | |
532 case VideoFrame::kAPlane: | |
533 return gfx::Size(width, height); | |
534 case VideoFrame::kUPlane: | |
535 case VideoFrame::kVPlane: | |
536 return gfx::Size(width / 2, height / 2); | |
537 default: | |
538 break; | |
539 } | |
540 break; | |
541 case VideoFrame::YV16: | |
542 switch (plane) { | |
543 case VideoFrame::kYPlane: | |
544 return gfx::Size(width, height); | |
545 case VideoFrame::kUPlane: | |
546 case VideoFrame::kVPlane: | |
547 return gfx::Size(width / 2, height); | |
548 default: | |
549 break; | |
550 } | |
551 break; | |
552 case VideoFrame::NV12: | |
553 switch (plane) { | |
554 case VideoFrame::kYPlane: | |
555 return gfx::Size(width, height); | |
556 case VideoFrame::kUVPlane: | |
557 return gfx::Size(width, height / 2); | |
558 default: | |
559 break; | |
560 } | |
561 break; | |
562 case VideoFrame::UNKNOWN: | |
563 case VideoFrame::NATIVE_TEXTURE: | |
564 #if defined(VIDEO_HOLE) | |
565 case VideoFrame::HOLE: | |
566 #endif // defined(VIDEO_HOLE) | |
567 break; | |
568 } | |
569 NOTREACHED() << "Unsupported video frame format/plane: " | |
570 << format << "/" << plane; | |
571 return gfx::Size(); | |
572 } | 567 } |
573 | 568 |
574 size_t VideoFrame::PlaneAllocationSize(Format format, | 569 size_t VideoFrame::PlaneAllocationSize(Format format, |
575 size_t plane, | 570 size_t plane, |
576 const gfx::Size& coded_size) { | 571 const gfx::Size& coded_size) { |
577 // VideoFrame formats are (so far) all YUV and 1 byte per sample. | 572 // VideoFrame formats are (so far) all YUV and 1 byte per sample. |
578 return PlaneSize(format, plane, coded_size).GetArea(); | 573 return PlaneSize(format, plane, coded_size).GetArea(); |
579 } | 574 } |
580 | 575 |
581 // static | 576 // static |
582 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format, size_t plane) { | 577 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format, size_t plane) { |
583 switch (format) { | 578 DCHECK(IsValidPlane(plane, format)); |
584 case VideoFrame::YV24: | 579 const int bits_per_element = 8 * BytesPerElement(format, plane); |
585 switch (plane) { | 580 const int pixels_per_element = SampleSize(format, plane).GetArea(); |
586 case kYPlane: | 581 DCHECK(bits_per_element % pixels_per_element == 0); |
587 case kUPlane: | 582 return bits_per_element / pixels_per_element; |
588 case kVPlane: | |
589 return 8; | |
590 default: | |
591 break; | |
592 } | |
593 break; | |
594 case VideoFrame::YV12: | |
595 case VideoFrame::YV16: | |
596 case VideoFrame::I420: | |
597 case VideoFrame::YV12J: | |
598 switch (plane) { | |
599 case kYPlane: | |
600 return 8; | |
601 case kUPlane: | |
602 case kVPlane: | |
603 return 2; | |
604 default: | |
605 break; | |
606 } | |
607 break; | |
608 case VideoFrame::YV12A: | |
609 switch (plane) { | |
610 case kYPlane: | |
611 case kAPlane: | |
612 return 8; | |
613 case kUPlane: | |
614 case kVPlane: | |
615 return 2; | |
616 default: | |
617 break; | |
618 } | |
619 break; | |
620 case VideoFrame::NV12: | |
621 switch (plane) { | |
622 case kYPlane: | |
623 return 8; | |
624 case kUVPlane: | |
625 return 4; | |
626 default: | |
627 break; | |
628 } | |
629 break; | |
630 case VideoFrame::UNKNOWN: | |
631 #if defined(VIDEO_HOLE) | |
632 case VideoFrame::HOLE: | |
633 #endif // defined(VIDEO_HOLE) | |
634 case VideoFrame::NATIVE_TEXTURE: | |
635 break; | |
636 } | |
637 NOTREACHED() << "Unsupported video frame format/plane: " | |
638 << format << "/" << plane; | |
639 return 0; | |
640 } | 583 } |
641 | 584 |
642 // Release data allocated by AllocateYUV(). | 585 // Release data allocated by AllocateYUV(). |
643 static void ReleaseData(uint8* data) { | 586 static void ReleaseData(uint8* data) { |
644 DCHECK(data); | 587 DCHECK(data); |
645 base::AlignedFree(data); | 588 base::AlignedFree(data); |
646 } | 589 } |
647 | 590 |
648 void VideoFrame::AllocateYUV() { | 591 void VideoFrame::AllocateYUV() { |
649 DCHECK(format_ == VideoFrame::YV12 || format_ == VideoFrame::YV16 || | 592 DCHECK(format_ == YV12 || format_ == YV16 || format_ == YV12A || |
650 format_ == VideoFrame::YV12A || format_ == VideoFrame::I420 || | 593 format_ == I420 || format_ == YV12J || format_ == YV24); |
651 format_ == VideoFrame::YV12J || format_ == VideoFrame::YV24); | 594 COMPILE_ASSERT(0 == kYPlane, y_plane_data_must_be_index_0); |
652 // Align Y rows at least at 16 byte boundaries. The stride for both | |
653 // YV12 and YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for | |
654 // U and V applies to two rows of Y (one byte of UV for 4 bytes of Y), so in | |
655 // the case of YV12 the strides are identical for the same width surface, but | |
656 // the number of bytes allocated for YV12 is 1/2 the amount for U & V as | |
657 // YV16. We also round the height of the surface allocated to be an even | |
658 // number to avoid any potential of faulting by code that attempts to access | |
659 // the Y values of the final row, but assumes that the last row of U & V | |
660 // applies to a full two rows of Y. YV12A is the same as YV12, but with an | |
661 // additional alpha plane that has the same size and alignment as the Y plane. | |
662 size_t y_stride = RoundUp(row_bytes(VideoFrame::kYPlane), | |
663 kFrameSizeAlignment); | |
664 size_t uv_stride = RoundUp(row_bytes(VideoFrame::kUPlane), | |
665 kFrameSizeAlignment); | |
666 | 595 |
667 // The *2 here is because some formats (e.g. h264) allow interlaced coding, | 596 size_t data_size = 0; |
668 // and then the size needs to be a multiple of two macroblocks (vertically). | 597 size_t offset[kMaxPlanes]; |
669 // See libavcodec/utils.c:avcodec_align_dimensions2(). | 598 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format_); ++plane) { |
670 size_t y_height = RoundUp(coded_size_.height(), kFrameSizeAlignment * 2); | 599 // The *2 in alignment for height is because some formats (e.g. h264) allow |
671 size_t uv_height = | 600 // interlaced coding, and then the size needs to be a multiple of two |
672 (format_ == VideoFrame::YV12 || format_ == VideoFrame::YV12A || | 601 // macroblocks (vertically). See |
673 format_ == VideoFrame::I420) | 602 // libavcodec/utils.c:avcodec_align_dimensions2(). |
674 ? y_height / 2 | 603 const size_t height = RoundUp(rows(plane), kFrameSizeAlignment * 2); |
675 : y_height; | 604 strides_[plane] = RoundUp(row_bytes(plane), kFrameSizeAlignment); |
676 size_t y_bytes = y_height * y_stride; | 605 offset[plane] = data_size; |
677 size_t uv_bytes = uv_height * uv_stride; | 606 data_size += height * strides_[plane]; |
678 size_t a_bytes = format_ == VideoFrame::YV12A ? y_bytes : 0; | 607 } |
679 | 608 |
680 // The extra line of UV being allocated is because h264 chroma MC | 609 // The extra line of UV being allocated is because h264 chroma MC |
681 // overreads by one line in some cases, see libavcodec/utils.c: | 610 // overreads by one line in some cases, see libavcodec/utils.c: |
682 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm: | 611 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm: |
683 // put_h264_chroma_mc4_ssse3(). | 612 // put_h264_chroma_mc4_ssse3(). |
684 const size_t data_size = | 613 DCHECK(IsValidPlane(kUPlane, format_)); |
685 y_bytes + (uv_bytes * 2 + uv_stride) + a_bytes + kFrameSizePadding; | 614 data_size += strides_[kUPlane] + kFrameSizePadding; |
615 | |
616 // FFmpeg expects the initialize allocation to be zero-initialized. Failure | |
617 // to do so can lead to unitialized value usage. See http://crbug.com/390941 | |
686 uint8* data = reinterpret_cast<uint8*>( | 618 uint8* data = reinterpret_cast<uint8*>( |
687 base::AlignedAlloc(data_size, kFrameAddressAlignment)); | 619 base::AlignedAlloc(data_size, kFrameAddressAlignment)); |
688 // FFmpeg expects the initialize allocation to be zero-initialized. Failure | |
689 // to do so can lead to unitialized value usage. See http://crbug.com/390941 | |
690 memset(data, 0, data_size); | 620 memset(data, 0, data_size); |
621 | |
622 for (size_t plane = 0; plane < VideoFrame::NumPlanes(format_); ++plane) | |
623 data_[plane] = data + offset[plane]; | |
624 | |
691 no_longer_needed_cb_ = base::Bind(&ReleaseData, data); | 625 no_longer_needed_cb_ = base::Bind(&ReleaseData, data); |
692 COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0); | |
693 data_[VideoFrame::kYPlane] = data; | |
694 data_[VideoFrame::kUPlane] = data + y_bytes; | |
695 data_[VideoFrame::kVPlane] = data + y_bytes + uv_bytes; | |
696 strides_[VideoFrame::kYPlane] = y_stride; | |
697 strides_[VideoFrame::kUPlane] = uv_stride; | |
698 strides_[VideoFrame::kVPlane] = uv_stride; | |
699 if (format_ == YV12A) { | |
700 data_[VideoFrame::kAPlane] = data + y_bytes + (2 * uv_bytes); | |
701 strides_[VideoFrame::kAPlane] = y_stride; | |
702 } | |
703 } | 626 } |
704 | 627 |
705 VideoFrame::VideoFrame(VideoFrame::Format format, | 628 VideoFrame::VideoFrame(VideoFrame::Format format, |
706 const gfx::Size& coded_size, | 629 const gfx::Size& coded_size, |
707 const gfx::Rect& visible_rect, | 630 const gfx::Rect& visible_rect, |
708 const gfx::Size& natural_size, | 631 const gfx::Size& natural_size, |
709 scoped_ptr<gpu::MailboxHolder> mailbox_holder, | 632 scoped_ptr<gpu::MailboxHolder> mailbox_holder, |
710 base::TimeDelta timestamp, | 633 base::TimeDelta timestamp, |
711 bool end_of_stream) | 634 bool end_of_stream) |
712 : format_(format), | 635 : format_(format), |
(...skipping 30 matching lines...) Expand all Loading... | |
743 bool VideoFrame::IsValidPlane(size_t plane, VideoFrame::Format format) { | 666 bool VideoFrame::IsValidPlane(size_t plane, VideoFrame::Format format) { |
744 return (plane < NumPlanes(format)); | 667 return (plane < NumPlanes(format)); |
745 } | 668 } |
746 | 669 |
747 int VideoFrame::stride(size_t plane) const { | 670 int VideoFrame::stride(size_t plane) const { |
748 DCHECK(IsValidPlane(plane, format_)); | 671 DCHECK(IsValidPlane(plane, format_)); |
749 return strides_[plane]; | 672 return strides_[plane]; |
750 } | 673 } |
751 | 674 |
752 // static | 675 // static |
753 size_t VideoFrame::RowBytes(size_t plane, VideoFrame::Format format, | 676 size_t VideoFrame::RowBytes(size_t plane, |
677 VideoFrame::Format format, | |
754 int width) { | 678 int width) { |
755 DCHECK(IsValidPlane(plane, format)); | 679 DCHECK(IsValidPlane(plane, format)); |
756 switch (format) { | 680 return BytesPerElement(format, plane) * Columns(plane, format, width); |
757 case VideoFrame::YV24: | |
758 switch (plane) { | |
759 case kYPlane: | |
760 case kUPlane: | |
761 case kVPlane: | |
762 return width; | |
763 default: | |
764 break; | |
765 } | |
766 break; | |
767 case VideoFrame::YV12: | |
768 case VideoFrame::YV16: | |
769 case VideoFrame::I420: | |
770 case VideoFrame::YV12J: | |
771 switch (plane) { | |
772 case kYPlane: | |
773 return width; | |
774 case kUPlane: | |
775 case kVPlane: | |
776 return RoundUp(width, 2) / 2; | |
777 default: | |
778 break; | |
779 } | |
780 break; | |
781 case VideoFrame::YV12A: | |
782 switch (plane) { | |
783 case kYPlane: | |
784 case kAPlane: | |
785 return width; | |
786 case kUPlane: | |
787 case kVPlane: | |
788 return RoundUp(width, 2) / 2; | |
789 default: | |
790 break; | |
791 } | |
792 break; | |
793 case VideoFrame::NV12: | |
794 switch (plane) { | |
795 case kYPlane: | |
796 case kUVPlane: | |
797 return width; | |
798 default: | |
799 break; | |
800 } | |
801 break; | |
802 case VideoFrame::UNKNOWN: | |
803 #if defined(VIDEO_HOLE) | |
804 case VideoFrame::HOLE: | |
805 #endif // defined(VIDEO_HOLE) | |
806 case VideoFrame::NATIVE_TEXTURE: | |
807 break; | |
808 } | |
809 NOTREACHED() << "Unsupported video frame format/plane: " << format << "/" | |
810 << plane; | |
811 return 0; | |
812 } | 681 } |
813 | 682 |
814 int VideoFrame::row_bytes(size_t plane) const { | 683 int VideoFrame::row_bytes(size_t plane) const { |
815 return RowBytes(plane, format_, coded_size_.width()); | 684 return RowBytes(plane, format_, coded_size_.width()); |
816 } | 685 } |
817 | 686 |
818 // static | 687 // static |
819 size_t VideoFrame::Rows(size_t plane, VideoFrame::Format format, int height) { | 688 size_t VideoFrame::Rows(size_t plane, VideoFrame::Format format, int height) { |
820 DCHECK(IsValidPlane(plane, format)); | 689 DCHECK(IsValidPlane(plane, format)); |
821 switch (format) { | 690 const int sample_height = SampleSize(format, plane).height(); |
822 case VideoFrame::YV24: | 691 return RoundUp(height, sample_height) / sample_height; |
823 case VideoFrame::YV16: | 692 } |
824 switch (plane) { | 693 |
825 case kYPlane: | 694 // static |
826 case kUPlane: | 695 size_t VideoFrame::Columns(size_t plane, Format format, int width) { |
827 case kVPlane: | 696 DCHECK(IsValidPlane(plane, format)); |
828 return height; | 697 const int sample_width = SampleSize(format, plane).width(); |
829 default: | 698 return RoundUp(width, sample_width) / sample_width; |
830 break; | |
831 } | |
832 break; | |
833 case VideoFrame::YV12: | |
834 case VideoFrame::YV12J: | |
835 case VideoFrame::I420: | |
836 switch (plane) { | |
837 case kYPlane: | |
838 return height; | |
839 case kUPlane: | |
840 case kVPlane: | |
841 return RoundUp(height, 2) / 2; | |
842 default: | |
843 break; | |
844 } | |
845 break; | |
846 case VideoFrame::YV12A: | |
847 switch (plane) { | |
848 case kYPlane: | |
849 case kAPlane: | |
850 return height; | |
851 case kUPlane: | |
852 case kVPlane: | |
853 return RoundUp(height, 2) / 2; | |
854 default: | |
855 break; | |
856 } | |
857 break; | |
858 case VideoFrame::NV12: | |
859 switch (plane) { | |
860 case kYPlane: | |
861 return height; | |
862 case kUVPlane: | |
863 return RoundUp(height, 2) / 2; | |
864 default: | |
865 break; | |
866 } | |
867 break; | |
868 case VideoFrame::UNKNOWN: | |
869 #if defined(VIDEO_HOLE) | |
870 case VideoFrame::HOLE: | |
871 #endif // defined(VIDEO_HOLE) | |
872 case VideoFrame::NATIVE_TEXTURE: | |
873 break; | |
874 } | |
875 NOTREACHED() << "Unsupported video frame format/plane: " << format << "/" | |
876 << plane; | |
877 return 0; | |
878 } | 699 } |
879 | 700 |
880 int VideoFrame::rows(size_t plane) const { | 701 int VideoFrame::rows(size_t plane) const { |
881 return Rows(plane, format_, coded_size_.height()); | 702 return Rows(plane, format_, coded_size_.height()); |
882 } | 703 } |
883 | 704 |
884 uint8* VideoFrame::data(size_t plane) const { | 705 const uint8* VideoFrame::data(size_t plane) const { |
885 DCHECK(IsValidPlane(plane, format_)); | 706 DCHECK(IsValidPlane(plane, format_)); |
886 return data_[plane]; | 707 return data_[plane]; |
887 } | 708 } |
888 | 709 |
710 uint8* VideoFrame::data(size_t plane) { | |
711 DCHECK(IsValidPlane(plane, format_)); | |
712 return data_[plane]; | |
713 } | |
714 | |
715 const uint8* VideoFrame::visible_data(size_t plane) const { | |
716 DCHECK(IsValidPlane(plane, format_)); | |
717 | |
718 // Calculate an offset that is properly aligned for all planes. | |
719 const gfx::Size alignment = CommonAlignment(format_); | |
720 const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()), | |
721 RoundDown(visible_rect_.y(), alignment.height())); | |
722 | |
723 const gfx::Size subsample = SampleSize(format_, plane); | |
724 DCHECK(offset.x() % subsample.width() == 0 && | |
725 offset.y() % subsample.height() == 0); | |
scherkus (not reviewing)
2014/10/22 23:01:50
ditto for DCHECK failure
magjed_chromium
2014/10/23 11:44:31
Done.
| |
726 return data(plane) + | |
727 stride(plane) * (offset.y() / subsample.height()) + // Row offset. | |
728 BytesPerElement(format_, plane) * // Column offset. | |
729 (offset.x() / subsample.width()); | |
730 } | |
731 | |
732 uint8* VideoFrame::visible_data(size_t plane) { | |
733 return const_cast<uint8*>( | |
734 static_cast<const VideoFrame*>(this)->visible_data(plane)); | |
735 } | |
736 | |
889 const gpu::MailboxHolder* VideoFrame::mailbox_holder() const { | 737 const gpu::MailboxHolder* VideoFrame::mailbox_holder() const { |
890 DCHECK_EQ(format_, NATIVE_TEXTURE); | 738 DCHECK_EQ(format_, NATIVE_TEXTURE); |
891 return mailbox_holder_.get(); | 739 return mailbox_holder_.get(); |
892 } | 740 } |
893 | 741 |
894 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const { | 742 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const { |
895 return shared_memory_handle_; | 743 return shared_memory_handle_; |
896 } | 744 } |
897 | 745 |
898 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) { | 746 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) { |
(...skipping 13 matching lines...) Expand all Loading... | |
912 } | 760 } |
913 #endif | 761 #endif |
914 | 762 |
915 #if defined(OS_MACOSX) | 763 #if defined(OS_MACOSX) |
916 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const { | 764 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const { |
917 return cv_pixel_buffer_.get(); | 765 return cv_pixel_buffer_.get(); |
918 } | 766 } |
919 #endif | 767 #endif |
920 | 768 |
921 void VideoFrame::HashFrameForTesting(base::MD5Context* context) { | 769 void VideoFrame::HashFrameForTesting(base::MD5Context* context) { |
922 for (int plane = 0; plane < kMaxPlanes; ++plane) { | 770 for (size_t plane = 0; plane < NumPlanes(format_); ++plane) { |
923 if (!IsValidPlane(plane, format_)) | |
924 break; | |
925 for (int row = 0; row < rows(plane); ++row) { | 771 for (int row = 0; row < rows(plane); ++row) { |
926 base::MD5Update(context, base::StringPiece( | 772 base::MD5Update(context, base::StringPiece( |
927 reinterpret_cast<char*>(data(plane) + stride(plane) * row), | 773 reinterpret_cast<char*>(data(plane) + stride(plane) * row), |
928 row_bytes(plane))); | 774 row_bytes(plane))); |
929 } | 775 } |
930 } | 776 } |
931 } | 777 } |
932 | 778 |
933 } // namespace media | 779 } // namespace media |
OLD | NEW |