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 "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/string_piece.h" | 8 #include "base/string_piece.h" |
9 #include "media/base/limits.h" | 9 #include "media/base/limits.h" |
10 #include "media/base/video_util.h" | 10 #include "media/base/video_util.h" |
11 #if !defined(OS_ANDROID) | 11 #if !defined(OS_ANDROID) |
12 #include "media/ffmpeg/ffmpeg_common.h" | 12 #include "media/ffmpeg/ffmpeg_common.h" |
13 #endif | 13 #endif |
14 | 14 |
15 #include <algorithm> | 15 #include <algorithm> |
16 | 16 |
17 namespace media { | 17 namespace media { |
18 | 18 |
19 // static | 19 // static |
20 scoped_refptr<VideoFrame> VideoFrame::CreateFrame( | 20 scoped_refptr<VideoFrame> VideoFrame::CreateFrame( |
21 VideoFrame::Format format, | 21 VideoFrame::Format format, |
22 size_t width, | 22 const gfx::Size& data_size, |
23 size_t height, | 23 const gfx::Size& natural_size, |
24 base::TimeDelta timestamp) { | 24 base::TimeDelta timestamp) { |
25 DCHECK(IsValidConfig(format, width, height)); | 25 DCHECK(IsValidConfig(format, data_size, natural_size)); |
26 scoped_refptr<VideoFrame> frame(new VideoFrame( | 26 scoped_refptr<VideoFrame> frame(new VideoFrame( |
27 format, width, height, timestamp)); | 27 format, data_size, natural_size, timestamp)); |
28 switch (format) { | 28 switch (format) { |
29 case VideoFrame::RGB32: | 29 case VideoFrame::RGB32: |
30 frame->AllocateRGB(4u); | 30 frame->AllocateRGB(4u); |
31 break; | 31 break; |
32 case VideoFrame::YV12: | 32 case VideoFrame::YV12: |
33 case VideoFrame::YV16: | 33 case VideoFrame::YV16: |
34 frame->AllocateYUV(); | 34 frame->AllocateYUV(); |
35 break; | 35 break; |
36 default: | 36 default: |
37 LOG(FATAL) << "Unsupported frame format: " << format; | 37 LOG(FATAL) << "Unsupported frame format: " << format; |
38 } | 38 } |
39 return frame; | 39 return frame; |
40 } | 40 } |
41 | 41 |
42 // static | 42 // static |
43 bool VideoFrame::IsValidConfig( | 43 bool VideoFrame::IsValidConfig(VideoFrame::Format format, |
44 VideoFrame::Format format, | 44 const gfx::Size& data_size, |
45 size_t width, | 45 const gfx::Size& natural_size) { |
46 size_t height) { | |
47 | |
48 return (format != VideoFrame::INVALID && | 46 return (format != VideoFrame::INVALID && |
49 width > 0 && height > 0 && | 47 data_size.width() > 0 && data_size.height() > 0 && |
50 width <= limits::kMaxDimension && height <= limits::kMaxDimension && | 48 data_size.width() <= limits::kMaxDimension && |
51 width * height <= limits::kMaxCanvas); | 49 data_size.height() <= limits::kMaxDimension && |
| 50 data_size.width() * data_size.height() <= limits::kMaxCanvas && |
| 51 natural_size.width() > 0 && natural_size.height() > 0 && |
| 52 natural_size.width() <= limits::kMaxDimension && |
| 53 natural_size.height() <= limits::kMaxDimension && |
| 54 natural_size.width() * natural_size.height() <= limits::kMaxCanvas); |
52 } | 55 } |
53 | 56 |
54 // static | 57 // static |
55 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture( | 58 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture( |
56 uint32 texture_id, | 59 uint32 texture_id, |
57 uint32 texture_target, | 60 uint32 texture_target, |
58 size_t width, | 61 const gfx::Size& data_size, |
59 size_t height, | 62 const gfx::Size& natural_size, |
60 base::TimeDelta timestamp, | 63 base::TimeDelta timestamp, |
61 const base::Closure& no_longer_needed) { | 64 const base::Closure& no_longer_needed) { |
62 scoped_refptr<VideoFrame> frame( | 65 scoped_refptr<VideoFrame> frame( |
63 new VideoFrame(NATIVE_TEXTURE, width, height, timestamp)); | 66 new VideoFrame(NATIVE_TEXTURE, data_size, natural_size, timestamp)); |
64 frame->texture_id_ = texture_id; | 67 frame->texture_id_ = texture_id; |
65 frame->texture_target_ = texture_target; | 68 frame->texture_target_ = texture_target; |
66 frame->texture_no_longer_needed_ = no_longer_needed; | 69 frame->texture_no_longer_needed_ = no_longer_needed; |
67 return frame; | 70 return frame; |
68 } | 71 } |
69 | 72 |
70 // static | 73 // static |
71 scoped_refptr<VideoFrame> VideoFrame::CreateEmptyFrame() { | 74 scoped_refptr<VideoFrame> VideoFrame::CreateEmptyFrame() { |
72 return new VideoFrame( | 75 return new VideoFrame( |
73 VideoFrame::EMPTY, 0, 0, base::TimeDelta()); | 76 VideoFrame::EMPTY, gfx::Size(), gfx::Size(), base::TimeDelta()); |
74 } | 77 } |
75 | 78 |
76 // static | 79 // static |
77 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(int width, int height) { | 80 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame( |
78 DCHECK_GT(width, 0); | 81 const gfx::Size& data_size) { |
79 DCHECK_GT(height, 0); | 82 DCHECK(IsValidConfig(VideoFrame::YV12, data_size, data_size)); |
80 | 83 |
81 // Create our frame. | 84 // Create our frame. |
82 const base::TimeDelta kZero; | 85 const base::TimeDelta kZero; |
83 scoped_refptr<VideoFrame> frame = | 86 scoped_refptr<VideoFrame> frame = |
84 VideoFrame::CreateFrame(VideoFrame::YV12, width, height, kZero); | 87 VideoFrame::CreateFrame(VideoFrame::YV12, data_size, data_size, kZero); |
85 | 88 |
86 // Now set the data to YUV(0,128,128). | 89 // Now set the data to YUV(0,128,128). |
87 const uint8 kBlackY = 0x00; | 90 const uint8 kBlackY = 0x00; |
88 const uint8 kBlackUV = 0x80; | 91 const uint8 kBlackUV = 0x80; |
89 FillYUV(frame, kBlackY, kBlackUV, kBlackUV); | 92 FillYUV(frame, kBlackY, kBlackUV, kBlackUV); |
90 return frame; | 93 return frame; |
91 } | 94 } |
92 | 95 |
93 static inline size_t RoundUp(size_t value, size_t alignment) { | 96 static inline size_t RoundUp(size_t value, size_t alignment) { |
94 // Check that |alignment| is a power of 2. | 97 // Check that |alignment| is a power of 2. |
95 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); | 98 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); |
96 return ((value + (alignment - 1)) & ~(alignment-1)); | 99 return ((value + (alignment - 1)) & ~(alignment-1)); |
97 } | 100 } |
98 | 101 |
99 static const int kFrameSizeAlignment = 16; | 102 static const int kFrameSizeAlignment = 16; |
100 // Allows faster SIMD YUV convert. Also, FFmpeg overreads/-writes occasionally. | 103 // Allows faster SIMD YUV convert. Also, FFmpeg overreads/-writes occasionally. |
101 static const int kFramePadBytes = 15; | 104 static const int kFramePadBytes = 15; |
102 | 105 |
103 void VideoFrame::AllocateRGB(size_t bytes_per_pixel) { | 106 void VideoFrame::AllocateRGB(size_t bytes_per_pixel) { |
104 // Round up to align at least at a 16-byte boundary for each row. | 107 // Round up to align at least at a 16-byte boundary for each row. |
105 // This is sufficient for MMX and SSE2 reads (movq/movdqa). | 108 // This is sufficient for MMX and SSE2 reads (movq/movdqa). |
106 size_t bytes_per_row = RoundUp(width_, kFrameSizeAlignment) * bytes_per_pixel; | 109 size_t bytes_per_row = RoundUp(data_size_.width(), |
107 size_t aligned_height = RoundUp(height_, kFrameSizeAlignment); | 110 kFrameSizeAlignment) * bytes_per_pixel; |
| 111 size_t aligned_height = RoundUp(data_size_.height(), kFrameSizeAlignment); |
108 strides_[VideoFrame::kRGBPlane] = bytes_per_row; | 112 strides_[VideoFrame::kRGBPlane] = bytes_per_row; |
109 #if !defined(OS_ANDROID) | 113 #if !defined(OS_ANDROID) |
110 // TODO(dalecurtis): use DataAligned or so, so this #ifdef hackery | 114 // TODO(dalecurtis): use DataAligned or so, so this #ifdef hackery |
111 // doesn't need to be repeated in every single user of aligned data. | 115 // doesn't need to be repeated in every single user of aligned data. |
112 data_[VideoFrame::kRGBPlane] = reinterpret_cast<uint8*>( | 116 data_[VideoFrame::kRGBPlane] = reinterpret_cast<uint8*>( |
113 av_malloc(bytes_per_row * aligned_height + kFramePadBytes)); | 117 av_malloc(bytes_per_row * aligned_height + kFramePadBytes)); |
114 #else | 118 #else |
115 data_[VideoFrame::kRGBPlane] = new uint8_t[bytes_per_row * aligned_height]; | 119 data_[VideoFrame::kRGBPlane] = new uint8_t[bytes_per_row * aligned_height]; |
116 #endif | 120 #endif |
117 DCHECK(!(reinterpret_cast<intptr_t>(data_[VideoFrame::kRGBPlane]) & 7)); | 121 DCHECK(!(reinterpret_cast<intptr_t>(data_[VideoFrame::kRGBPlane]) & 7)); |
(...skipping 11 matching lines...) Expand all Loading... |
129 // number to avoid any potential of faulting by code that attempts to access | 133 // number to avoid any potential of faulting by code that attempts to access |
130 // the Y values of the final row, but assumes that the last row of U & V | 134 // the Y values of the final row, but assumes that the last row of U & V |
131 // applies to a full two rows of Y. | 135 // applies to a full two rows of Y. |
132 size_t y_stride = RoundUp(row_bytes(VideoFrame::kYPlane), | 136 size_t y_stride = RoundUp(row_bytes(VideoFrame::kYPlane), |
133 kFrameSizeAlignment); | 137 kFrameSizeAlignment); |
134 size_t uv_stride = RoundUp(row_bytes(VideoFrame::kUPlane), | 138 size_t uv_stride = RoundUp(row_bytes(VideoFrame::kUPlane), |
135 kFrameSizeAlignment); | 139 kFrameSizeAlignment); |
136 // The *2 here is because some formats (e.g. h264) allow interlaced coding, | 140 // The *2 here is because some formats (e.g. h264) allow interlaced coding, |
137 // and then the size needs to be a multiple of two macroblocks (vertically). | 141 // and then the size needs to be a multiple of two macroblocks (vertically). |
138 // See libavcodec/utils.c:avcodec_align_dimensions2(). | 142 // See libavcodec/utils.c:avcodec_align_dimensions2(). |
139 size_t y_height = RoundUp(height_, kFrameSizeAlignment * 2); | 143 size_t y_height = RoundUp(data_size_.height(), kFrameSizeAlignment * 2); |
140 size_t uv_height = format_ == VideoFrame::YV12 ? y_height / 2 : y_height; | 144 size_t uv_height = format_ == VideoFrame::YV12 ? y_height / 2 : y_height; |
141 size_t y_bytes = y_height * y_stride; | 145 size_t y_bytes = y_height * y_stride; |
142 size_t uv_bytes = uv_height * uv_stride; | 146 size_t uv_bytes = uv_height * uv_stride; |
143 | 147 |
144 #if !defined(OS_ANDROID) | 148 #if !defined(OS_ANDROID) |
145 // TODO(dalecurtis): use DataAligned or so, so this #ifdef hackery | 149 // TODO(dalecurtis): use DataAligned or so, so this #ifdef hackery |
146 // doesn't need to be repeated in every single user of aligned data. | 150 // doesn't need to be repeated in every single user of aligned data. |
147 // The extra line of UV being allocated is because h264 chroma MC | 151 // The extra line of UV being allocated is because h264 chroma MC |
148 // overreads by one line in some cases, see libavcodec/utils.c: | 152 // overreads by one line in some cases, see libavcodec/utils.c: |
149 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm: | 153 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm: |
150 // put_h264_chroma_mc4_ssse3(). | 154 // put_h264_chroma_mc4_ssse3(). |
151 uint8* data = reinterpret_cast<uint8*>( | 155 uint8* data = reinterpret_cast<uint8*>( |
152 av_malloc(y_bytes + (uv_bytes * 2 + uv_stride) + kFramePadBytes)); | 156 av_malloc(y_bytes + (uv_bytes * 2 + uv_stride) + kFramePadBytes)); |
153 #else | 157 #else |
154 uint8* data = new uint8_t[y_bytes + (uv_bytes * 2)]; | 158 uint8* data = new uint8_t[y_bytes + (uv_bytes * 2)]; |
155 #endif | 159 #endif |
156 COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0); | 160 COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0); |
157 data_[VideoFrame::kYPlane] = data; | 161 data_[VideoFrame::kYPlane] = data; |
158 data_[VideoFrame::kUPlane] = data + y_bytes; | 162 data_[VideoFrame::kUPlane] = data + y_bytes; |
159 data_[VideoFrame::kVPlane] = data + y_bytes + uv_bytes; | 163 data_[VideoFrame::kVPlane] = data + y_bytes + uv_bytes; |
160 strides_[VideoFrame::kYPlane] = y_stride; | 164 strides_[VideoFrame::kYPlane] = y_stride; |
161 strides_[VideoFrame::kUPlane] = uv_stride; | 165 strides_[VideoFrame::kUPlane] = uv_stride; |
162 strides_[VideoFrame::kVPlane] = uv_stride; | 166 strides_[VideoFrame::kVPlane] = uv_stride; |
163 } | 167 } |
164 | 168 |
165 VideoFrame::VideoFrame(VideoFrame::Format format, | 169 VideoFrame::VideoFrame(VideoFrame::Format format, |
166 size_t width, | 170 const gfx::Size& data_size, |
167 size_t height, | 171 const gfx::Size& natural_size, |
168 base::TimeDelta timestamp) | 172 base::TimeDelta timestamp) |
169 : format_(format), | 173 : format_(format), |
170 width_(width), | 174 data_size_(data_size), |
171 height_(height), | 175 natural_size_(natural_size), |
172 texture_id_(0), | 176 texture_id_(0), |
173 texture_target_(0), | 177 texture_target_(0), |
174 timestamp_(timestamp) { | 178 timestamp_(timestamp) { |
175 memset(&strides_, 0, sizeof(strides_)); | 179 memset(&strides_, 0, sizeof(strides_)); |
176 memset(&data_, 0, sizeof(data_)); | 180 memset(&data_, 0, sizeof(data_)); |
177 } | 181 } |
178 | 182 |
179 VideoFrame::~VideoFrame() { | 183 VideoFrame::~VideoFrame() { |
180 if (format_ == NATIVE_TEXTURE && !texture_no_longer_needed_.is_null()) { | 184 if (format_ == NATIVE_TEXTURE && !texture_no_longer_needed_.is_null()) { |
181 texture_no_longer_needed_.Run(); | 185 texture_no_longer_needed_.Run(); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
216 return false; | 220 return false; |
217 } | 221 } |
218 | 222 |
219 int VideoFrame::stride(size_t plane) const { | 223 int VideoFrame::stride(size_t plane) const { |
220 DCHECK(IsValidPlane(plane)); | 224 DCHECK(IsValidPlane(plane)); |
221 return strides_[plane]; | 225 return strides_[plane]; |
222 } | 226 } |
223 | 227 |
224 int VideoFrame::row_bytes(size_t plane) const { | 228 int VideoFrame::row_bytes(size_t plane) const { |
225 DCHECK(IsValidPlane(plane)); | 229 DCHECK(IsValidPlane(plane)); |
| 230 int width = data_size_.width(); |
226 switch (format_) { | 231 switch (format_) { |
227 // 32bpp. | 232 // 32bpp. |
228 case RGB32: | 233 case RGB32: |
229 return width_ * 4; | 234 return width * 4; |
230 | 235 |
231 // Planar, 8bpp. | 236 // Planar, 8bpp. |
232 case YV12: | 237 case YV12: |
233 case YV16: | 238 case YV16: |
234 if (plane == kYPlane) | 239 if (plane == kYPlane) |
235 return width_; | 240 return width; |
236 return RoundUp(width_, 2) / 2; | 241 return RoundUp(width, 2) / 2; |
237 | 242 |
238 default: | 243 default: |
239 break; | 244 break; |
240 } | 245 } |
241 | 246 |
242 // Intentionally leave out non-production formats. | 247 // Intentionally leave out non-production formats. |
243 NOTREACHED() << "Unsupported video frame format: " << format_; | 248 NOTREACHED() << "Unsupported video frame format: " << format_; |
244 return 0; | 249 return 0; |
245 } | 250 } |
246 | 251 |
247 int VideoFrame::rows(size_t plane) const { | 252 int VideoFrame::rows(size_t plane) const { |
248 DCHECK(IsValidPlane(plane)); | 253 DCHECK(IsValidPlane(plane)); |
| 254 int height = data_size_.height(); |
249 switch (format_) { | 255 switch (format_) { |
250 case RGB32: | 256 case RGB32: |
251 case YV16: | 257 case YV16: |
252 return height_; | 258 return height; |
253 | 259 |
254 case YV12: | 260 case YV12: |
255 if (plane == kYPlane) | 261 if (plane == kYPlane) |
256 return height_; | 262 return height; |
257 return RoundUp(height_, 2) / 2; | 263 return RoundUp(height, 2) / 2; |
258 | 264 |
259 default: | 265 default: |
260 break; | 266 break; |
261 } | 267 } |
262 | 268 |
263 // Intentionally leave out non-production formats. | 269 // Intentionally leave out non-production formats. |
264 NOTREACHED() << "Unsupported video frame format: " << format_; | 270 NOTREACHED() << "Unsupported video frame format: " << format_; |
265 return 0; | 271 return 0; |
266 } | 272 } |
267 | 273 |
(...skipping 22 matching lines...) Expand all Loading... |
290 break; | 296 break; |
291 for(int row = 0; row < rows(plane); row++) { | 297 for(int row = 0; row < rows(plane); row++) { |
292 base::MD5Update(context, base::StringPiece( | 298 base::MD5Update(context, base::StringPiece( |
293 reinterpret_cast<char*>(data(plane) + stride(plane) * row), | 299 reinterpret_cast<char*>(data(plane) + stride(plane) * row), |
294 row_bytes(plane))); | 300 row_bytes(plane))); |
295 } | 301 } |
296 } | 302 } |
297 } | 303 } |
298 | 304 |
299 } // namespace media | 305 } // namespace media |
OLD | NEW |