| 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) |
| 12 #include "media/ffmpeg/ffmpeg_common.h" |
| 13 #endif |
| 11 | 14 |
| 12 namespace media { | 15 namespace media { |
| 13 | 16 |
| 17 static inline size_t RoundUp(size_t value, size_t alignment) { |
| 18 // Check that |alignment| is a power of 2. |
| 19 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); |
| 20 return ((value + (alignment - 1)) & ~(alignment-1)); |
| 21 } |
| 22 |
| 14 // static | 23 // static |
| 15 scoped_refptr<VideoFrame> VideoFrame::CreateFrame( | 24 scoped_refptr<VideoFrame> VideoFrame::CreateFrame( |
| 16 VideoFrame::Format format, | 25 VideoFrame::Format format, |
| 17 size_t width, | 26 size_t width, |
| 18 size_t height, | 27 size_t height, |
| 28 size_t bytes_per_row, |
| 29 size_t bytes_per_uv_row, |
| 30 size_t aligned_height, |
| 19 base::TimeDelta timestamp, | 31 base::TimeDelta timestamp, |
| 20 base::TimeDelta duration) { | 32 base::TimeDelta duration) { |
| 21 DCHECK(IsValidConfig(format, width, height)); | 33 DCHECK(IsValidConfig(format, width, height, bytes_per_row, bytes_per_uv_row, |
| 34 aligned_height)); |
| 22 scoped_refptr<VideoFrame> frame(new VideoFrame( | 35 scoped_refptr<VideoFrame> frame(new VideoFrame( |
| 23 format, width, height, timestamp, duration)); | 36 format, width, height, timestamp, duration)); |
| 24 switch (format) { | 37 switch (format) { |
| 25 case VideoFrame::RGB32: | 38 case VideoFrame::RGB32: |
| 26 frame->AllocateRGB(4u); | 39 frame->AllocateRGB(bytes_per_row ? bytes_per_row : |
| 40 RoundUp(frame->row_bytes(VideoFrame::kRGBPlane), |
| 41 8), |
| 42 aligned_height ? aligned_height : height); |
| 27 break; | 43 break; |
| 28 case VideoFrame::YV12: | 44 case VideoFrame::YV12: |
| 29 case VideoFrame::YV16: | 45 case VideoFrame::YV16: |
| 30 frame->AllocateYUV(); | 46 frame->AllocateYUV(bytes_per_row ? bytes_per_row : |
| 47 RoundUp(frame->row_bytes(VideoFrame::kYPlane), 4), |
| 48 bytes_per_uv_row ? bytes_per_uv_row : |
| 49 RoundUp(frame->row_bytes(VideoFrame::kUPlane), 4), |
| 50 aligned_height ? aligned_height : height); |
| 31 break; | 51 break; |
| 32 default: | 52 default: |
| 33 LOG(FATAL) << "Unsupported frame format: " << format; | 53 LOG(FATAL) << "Unsupported frame format: " << format; |
| 34 } | 54 } |
| 35 return frame; | 55 return frame; |
| 36 } | 56 } |
| 37 | 57 |
| 38 // static | 58 // static |
| 39 bool VideoFrame::IsValidConfig( | 59 bool VideoFrame::IsValidConfig( |
| 40 VideoFrame::Format format, | 60 VideoFrame::Format format, |
| 41 size_t width, | 61 size_t width, |
| 42 size_t height) { | 62 size_t height, |
| 63 size_t bytes_per_row, |
| 64 size_t bytes_per_uv_row, |
| 65 size_t aligned_height) { |
| 43 | 66 |
| 44 return (format != VideoFrame::INVALID && | 67 if (!(format != VideoFrame::INVALID && |
| 45 width > 0 && height > 0 && | 68 width > 0 && height > 0 && |
| 46 width <= limits::kMaxDimension && height <= limits::kMaxDimension && | 69 width <= limits::kMaxDimension && height <= limits::kMaxDimension && |
| 47 width * height <= limits::kMaxCanvas); | 70 width * height <= limits::kMaxCanvas)) |
| 71 return false; |
| 72 |
| 73 switch (format) { |
| 74 case VideoFrame::RGB32: |
| 75 if ((bytes_per_row != 0 && bytes_per_row < width) || |
| 76 (aligned_height != 0 && aligned_height < height)) |
| 77 return false; |
| 78 break; |
| 79 case VideoFrame::YV12: |
| 80 case VideoFrame::YV16: |
| 81 if ((bytes_per_row != 0 && bytes_per_row < width) || |
| 82 (bytes_per_uv_row != 0 && bytes_per_uv_row < RoundUp(width, 2) / 2) || |
| 83 (aligned_height != 0 && aligned_height < height)) |
| 84 return false; |
| 85 break; |
| 86 default: |
| 87 break; |
| 88 } |
| 89 |
| 90 return true; |
| 48 } | 91 } |
| 49 | 92 |
| 50 // static | 93 // static |
| 51 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture( | 94 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture( |
| 52 uint32 texture_id, | 95 uint32 texture_id, |
| 53 uint32 texture_target, | 96 uint32 texture_target, |
| 54 size_t width, | 97 size_t width, |
| 55 size_t height, | 98 size_t height, |
| 56 base::TimeDelta timestamp, | 99 base::TimeDelta timestamp, |
| 57 base::TimeDelta duration, | 100 base::TimeDelta duration, |
| (...skipping 13 matching lines...) Expand all Loading... |
| 71 } | 114 } |
| 72 | 115 |
| 73 // static | 116 // static |
| 74 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(int width, int height) { | 117 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(int width, int height) { |
| 75 DCHECK_GT(width, 0); | 118 DCHECK_GT(width, 0); |
| 76 DCHECK_GT(height, 0); | 119 DCHECK_GT(height, 0); |
| 77 | 120 |
| 78 // Create our frame. | 121 // Create our frame. |
| 79 const base::TimeDelta kZero; | 122 const base::TimeDelta kZero; |
| 80 scoped_refptr<VideoFrame> frame = | 123 scoped_refptr<VideoFrame> frame = |
| 81 VideoFrame::CreateFrame(VideoFrame::YV12, width, height, kZero, kZero); | 124 VideoFrame::CreateFrame(VideoFrame::YV12, width, height, |
| 125 0, 0, 0, kZero, kZero); |
| 82 | 126 |
| 83 // Now set the data to YUV(0,128,128). | 127 // Now set the data to YUV(0,128,128). |
| 84 const uint8 kBlackY = 0x00; | 128 const uint8 kBlackY = 0x00; |
| 85 const uint8 kBlackUV = 0x80; | 129 const uint8 kBlackUV = 0x80; |
| 86 FillYUV(frame, kBlackY, kBlackUV, kBlackUV); | 130 FillYUV(frame, kBlackY, kBlackUV, kBlackUV); |
| 87 return frame; | 131 return frame; |
| 88 } | 132 } |
| 89 | 133 |
| 90 static inline size_t RoundUp(size_t value, size_t alignment) { | 134 void VideoFrame::AllocateRGB(size_t bytes_per_row, size_t aligned_height) { |
| 91 // Check that |alignment| is a power of 2. | |
| 92 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); | |
| 93 return ((value + (alignment - 1)) & ~(alignment-1)); | |
| 94 } | |
| 95 | |
| 96 void VideoFrame::AllocateRGB(size_t bytes_per_pixel) { | |
| 97 // Round up to align at a 64-bit (8 byte) boundary for each row. This | 135 // Round up to align at a 64-bit (8 byte) boundary for each row. This |
| 98 // is sufficient for MMX reads (movq). | 136 // is sufficient for MMX reads (movq). |
| 99 size_t bytes_per_row = RoundUp(width_ * bytes_per_pixel, 8); | |
| 100 strides_[VideoFrame::kRGBPlane] = bytes_per_row; | 137 strides_[VideoFrame::kRGBPlane] = bytes_per_row; |
| 101 data_[VideoFrame::kRGBPlane] = new uint8[bytes_per_row * height_]; | 138 #if !defined(OS_ANDROID) |
| 139 // TODO use DataAligned or so, so this #ifdef hackery doesn't need to be |
| 140 // repeated in every single user of aligned data. |
| 141 data_[VideoFrame::kRGBPlane] = reinterpret_cast<uint8 *>( |
| 142 av_malloc(bytes_per_row * aligned_height)); |
| 143 #else |
| 144 data_[VideoFrame::kRGBPlane] = new uint8_t[bytes_per_row * aligned_height]; |
| 145 #endif |
| 102 DCHECK(!(reinterpret_cast<intptr_t>(data_[VideoFrame::kRGBPlane]) & 7)); | 146 DCHECK(!(reinterpret_cast<intptr_t>(data_[VideoFrame::kRGBPlane]) & 7)); |
| 103 COMPILE_ASSERT(0 == VideoFrame::kRGBPlane, RGB_data_must_be_index_0); | 147 COMPILE_ASSERT(0 == VideoFrame::kRGBPlane, RGB_data_must_be_index_0); |
| 104 } | 148 } |
| 105 | 149 |
| 106 static const int kFramePadBytes = 15; // Allows faster SIMD YUV convert. | 150 static const int kFramePadBytes = 15; // Allows faster SIMD YUV convert. |
| 107 | 151 |
| 108 void VideoFrame::AllocateYUV() { | 152 void VideoFrame::AllocateYUV(size_t y_stride, size_t uv_stride, |
| 153 size_t aligned_height) { |
| 109 DCHECK(format_ == VideoFrame::YV12 || format_ == VideoFrame::YV16); | 154 DCHECK(format_ == VideoFrame::YV12 || format_ == VideoFrame::YV16); |
| 110 // Align Y rows at 32-bit (4 byte) boundaries. The stride for both YV12 and | 155 // Align Y rows at 32-bit (4 byte) boundaries. The stride for both YV12 and |
| 111 // YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for U and V | 156 // YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for U and V |
| 112 // applies to two rows of Y (one byte of UV for 4 bytes of Y), so in the | 157 // applies to two rows of Y (one byte of UV for 4 bytes of Y), so in the |
| 113 // case of YV12 the strides are identical for the same width surface, but the | 158 // case of YV12 the strides are identical for the same width surface, but the |
| 114 // number of bytes allocated for YV12 is 1/2 the amount for U & V as YV16. | 159 // number of bytes allocated for YV12 is 1/2 the amount for U & V as YV16. |
| 115 // We also round the height of the surface allocated to be an even number | 160 // We also round the height of the surface allocated to be an even number |
| 116 // to avoid any potential of faulting by code that attempts to access the Y | 161 // to avoid any potential of faulting by code that attempts to access the Y |
| 117 // values of the final row, but assumes that the last row of U & V applies to | 162 // values of the final row, but assumes that the last row of U & V applies to |
| 118 // a full two rows of Y. | 163 // a full two rows of Y. |
| 119 size_t y_height = rows(VideoFrame::kYPlane); | 164 size_t y_height = aligned_height; |
| 120 size_t y_stride = RoundUp(row_bytes(VideoFrame::kYPlane), 4); | 165 size_t uv_height = format_ == VideoFrame::YV12 ? |
| 121 size_t uv_stride = RoundUp(row_bytes(VideoFrame::kUPlane), 4); | 166 RoundUp(aligned_height, 2) / 2 : aligned_height; |
| 122 size_t uv_height = rows(VideoFrame::kUPlane); | |
| 123 size_t y_bytes = y_height * y_stride; | 167 size_t y_bytes = y_height * y_stride; |
| 124 size_t uv_bytes = uv_height * uv_stride; | 168 size_t uv_bytes = uv_height * uv_stride; |
| 125 | 169 |
| 126 uint8* data = new uint8[y_bytes + (uv_bytes * 2) + kFramePadBytes]; | 170 #if !defined(OS_ANDROID) |
| 171 // TODO use DataAligned or so, so this #ifdef hackery doesn't need to be |
| 172 // repeated in every single user of aligned data. |
| 173 uint8* data = reinterpret_cast<uint8 *>( |
| 174 av_malloc(y_bytes + (uv_bytes * 2) + kFramePadBytes)); |
| 175 #else |
| 176 uint8* data = new uint8_t[y_bytes + (uv_bytes * 2) + kFramePadBytes]; |
| 177 #endif |
| 127 COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0); | 178 COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0); |
| 128 data_[VideoFrame::kYPlane] = data; | 179 data_[VideoFrame::kYPlane] = data; |
| 129 data_[VideoFrame::kUPlane] = data + y_bytes; | 180 data_[VideoFrame::kUPlane] = data + y_bytes; |
| 130 data_[VideoFrame::kVPlane] = data + y_bytes + uv_bytes; | 181 data_[VideoFrame::kVPlane] = data + y_bytes + uv_bytes; |
| 131 strides_[VideoFrame::kYPlane] = y_stride; | 182 strides_[VideoFrame::kYPlane] = y_stride; |
| 132 strides_[VideoFrame::kUPlane] = uv_stride; | 183 strides_[VideoFrame::kUPlane] = uv_stride; |
| 133 strides_[VideoFrame::kVPlane] = uv_stride; | 184 strides_[VideoFrame::kVPlane] = uv_stride; |
| 134 } | 185 } |
| 135 | 186 |
| 136 VideoFrame::VideoFrame(VideoFrame::Format format, | 187 VideoFrame::VideoFrame(VideoFrame::Format format, |
| (...skipping 14 matching lines...) Expand all Loading... |
| 151 | 202 |
| 152 VideoFrame::~VideoFrame() { | 203 VideoFrame::~VideoFrame() { |
| 153 if (format_ == NATIVE_TEXTURE && !texture_no_longer_needed_.is_null()) { | 204 if (format_ == NATIVE_TEXTURE && !texture_no_longer_needed_.is_null()) { |
| 154 texture_no_longer_needed_.Run(); | 205 texture_no_longer_needed_.Run(); |
| 155 texture_no_longer_needed_.Reset(); | 206 texture_no_longer_needed_.Reset(); |
| 156 } | 207 } |
| 157 | 208 |
| 158 // In multi-plane allocations, only a single block of memory is allocated | 209 // In multi-plane allocations, only a single block of memory is allocated |
| 159 // on the heap, and other |data| pointers point inside the same, single block | 210 // on the heap, and other |data| pointers point inside the same, single block |
| 160 // so just delete index 0. | 211 // so just delete index 0. |
| 161 delete[] data_[0]; | 212 if (data_[0]) { |
| 213 #if !defined(OS_ANDROID) |
| 214 av_free(data_[0]); |
| 215 #else |
| 216 delete[] data_[0]; |
| 217 #endif |
| 218 } |
| 162 } | 219 } |
| 163 | 220 |
| 164 bool VideoFrame::IsValidPlane(size_t plane) const { | 221 bool VideoFrame::IsValidPlane(size_t plane) const { |
| 165 switch (format_) { | 222 switch (format_) { |
| 166 case RGB32: | 223 case RGB32: |
| 167 return plane == kRGBPlane; | 224 return plane == kRGBPlane; |
| 168 | 225 |
| 169 case YV12: | 226 case YV12: |
| 170 case YV16: | 227 case YV16: |
| 171 return plane == kYPlane || plane == kUPlane || plane == kVPlane; | 228 return plane == kYPlane || plane == kUPlane || plane == kVPlane; |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 257 break; | 314 break; |
| 258 for(int row = 0; row < rows(plane); row++) { | 315 for(int row = 0; row < rows(plane); row++) { |
| 259 base::MD5Update(context, base::StringPiece( | 316 base::MD5Update(context, base::StringPiece( |
| 260 reinterpret_cast<char*>(data(plane) + stride(plane) * row), | 317 reinterpret_cast<char*>(data(plane) + stride(plane) * row), |
| 261 row_bytes(plane))); | 318 row_bytes(plane))); |
| 262 } | 319 } |
| 263 } | 320 } |
| 264 } | 321 } |
| 265 | 322 |
| 266 } // namespace media | 323 } // namespace media |
| OLD | NEW |