3 * Copyright 2011 Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "talk/media/webrtc/webrtcvideoframe.h"
30 #include "libyuv/convert.h"
31 #include "libyuv/convert_from.h"
32 #include "libyuv/planar_functions.h"
33 #include "talk/media/base/videocapturer.h"
34 #include "talk/media/base/videocommon.h"
35 #include "webrtc/base/logging.h"
39 // Class that wraps ownerhip semantics of a buffer passed to it.
40 // * Buffers passed using Attach() become owned by this FrameBuffer and will be
41 // destroyed on FrameBuffer destruction.
42 // * Buffers passed using Alias() are not owned and will not be destroyed on
43 // FrameBuffer destruction, The buffer then must outlive the FrameBuffer.
44 class WebRtcVideoFrame::FrameBuffer {
47 explicit FrameBuffer(size_t length);
50 void Attach(uint8* data, size_t length);
51 void Alias(uint8* data, size_t length);
53 size_t length() const;
55 webrtc::VideoFrame* frame();
56 const webrtc::VideoFrame* frame() const;
59 rtc::scoped_ptr<uint8[]> owned_data_;
60 webrtc::VideoFrame video_frame_;
63 WebRtcVideoFrame::FrameBuffer::FrameBuffer() {}
65 WebRtcVideoFrame::FrameBuffer::FrameBuffer(size_t length) {
66 uint8* buffer = new uint8[length];
67 Attach(buffer, length);
70 WebRtcVideoFrame::FrameBuffer::~FrameBuffer() {
71 // Make sure that |video_frame_| doesn't delete the buffer, as |owned_data_|
72 // will release the buffer if this FrameBuffer owns it.
73 uint8_t* new_memory = NULL;
74 uint32_t new_length = 0;
75 uint32_t new_size = 0;
76 video_frame_.Swap(new_memory, new_length, new_size);
79 void WebRtcVideoFrame::FrameBuffer::Attach(uint8* data, size_t length) {
81 owned_data_.reset(data);
84 void WebRtcVideoFrame::FrameBuffer::Alias(uint8* data, size_t length) {
86 uint8_t* new_memory = reinterpret_cast<uint8_t*>(data);
87 uint32_t new_length = static_cast<uint32_t>(length);
88 uint32_t new_size = static_cast<uint32_t>(length);
89 video_frame_.Swap(new_memory, new_length, new_size);
92 uint8* WebRtcVideoFrame::FrameBuffer::data() {
93 return video_frame_.Buffer();
96 size_t WebRtcVideoFrame::FrameBuffer::length() const {
97 return video_frame_.Length();
100 webrtc::VideoFrame* WebRtcVideoFrame::FrameBuffer::frame() {
101 return &video_frame_;
104 const webrtc::VideoFrame* WebRtcVideoFrame::FrameBuffer::frame() const {
105 return &video_frame_;
108 WebRtcVideoFrame::WebRtcVideoFrame()
109 : video_buffer_(new RefCountedBuffer()), is_black_(false) {}
111 WebRtcVideoFrame::~WebRtcVideoFrame() {}
113 bool WebRtcVideoFrame::Init(
114 uint32 format, int w, int h, int dw, int dh, uint8* sample,
115 size_t sample_size, size_t pixel_width, size_t pixel_height,
116 int64 elapsed_time, int64 time_stamp, int rotation) {
117 return Reset(format, w, h, dw, dh, sample, sample_size, pixel_width,
118 pixel_height, elapsed_time, time_stamp, rotation);
121 bool WebRtcVideoFrame::Init(const CapturedFrame* frame, int dw, int dh) {
122 return Reset(frame->fourcc, frame->width, frame->height, dw, dh,
123 static_cast<uint8*>(frame->data), frame->data_size,
124 frame->pixel_width, frame->pixel_height, frame->elapsed_time,
125 frame->time_stamp, frame->rotation);
128 bool WebRtcVideoFrame::Alias(const CapturedFrame* frame, int dw, int dh) {
129 if (CanonicalFourCC(frame->fourcc) != FOURCC_I420 || frame->rotation != 0 ||
130 frame->width != dw || frame->height != dh) {
131 // TODO(fbarchard): Enable aliasing of more formats.
132 return Init(frame, dw, dh);
134 Alias(static_cast<uint8*>(frame->data),
147 bool WebRtcVideoFrame::InitToBlack(int w, int h, size_t pixel_width,
148 size_t pixel_height, int64 elapsed_time,
150 InitToEmptyBuffer(w, h, pixel_width, pixel_height, elapsed_time, time_stamp);
157 void WebRtcVideoFrame::Alias(
158 uint8* buffer, size_t buffer_size, int w, int h, size_t pixel_width,
159 size_t pixel_height, int64 elapsed_time, int64 time_stamp, int rotation) {
160 rtc::scoped_refptr<RefCountedBuffer> video_buffer(
161 new RefCountedBuffer());
162 video_buffer->Alias(buffer, buffer_size);
163 Attach(video_buffer.get(), buffer_size, w, h, pixel_width, pixel_height,
164 elapsed_time, time_stamp, rotation);
167 size_t WebRtcVideoFrame::GetWidth() const { return frame()->Width(); }
169 size_t WebRtcVideoFrame::GetHeight() const { return frame()->Height(); }
171 const uint8* WebRtcVideoFrame::GetYPlane() const {
172 uint8_t* buffer = frame()->Buffer();
176 const uint8* WebRtcVideoFrame::GetUPlane() const {
177 uint8_t* buffer = frame()->Buffer();
179 buffer += (frame()->Width() * frame()->Height());
184 const uint8* WebRtcVideoFrame::GetVPlane() const {
185 uint8_t* buffer = frame()->Buffer();
187 int uv_size = static_cast<int>(GetChromaSize());
188 buffer += frame()->Width() * frame()->Height() + uv_size;
193 uint8* WebRtcVideoFrame::GetYPlane() {
194 uint8_t* buffer = frame()->Buffer();
198 uint8* WebRtcVideoFrame::GetUPlane() {
199 uint8_t* buffer = frame()->Buffer();
201 buffer += (frame()->Width() * frame()->Height());
206 uint8* WebRtcVideoFrame::GetVPlane() {
207 uint8_t* buffer = frame()->Buffer();
209 int uv_size = static_cast<int>(GetChromaSize());
210 buffer += frame()->Width() * frame()->Height() + uv_size;
215 VideoFrame* WebRtcVideoFrame::Copy() const {
216 uint8* old_buffer = video_buffer_->data();
219 size_t new_buffer_size = video_buffer_->length();
221 WebRtcVideoFrame* ret_val = new WebRtcVideoFrame();
222 ret_val->Attach(video_buffer_.get(), new_buffer_size, frame()->Width(),
223 frame()->Height(), pixel_width_, pixel_height_, elapsed_time_,
224 time_stamp_, rotation_);
228 bool WebRtcVideoFrame::MakeExclusive() {
229 const size_t length = video_buffer_->length();
230 RefCountedBuffer* exclusive_buffer = new RefCountedBuffer(length);
231 memcpy(exclusive_buffer->data(), video_buffer_->data(), length);
232 Attach(exclusive_buffer, length, frame()->Width(), frame()->Height(),
233 pixel_width_, pixel_height_, elapsed_time_, time_stamp_, rotation_);
237 size_t WebRtcVideoFrame::CopyToBuffer(uint8* buffer, size_t size) const {
238 if (!frame()->Buffer()) {
242 size_t needed = frame()->Length();
243 if (needed <= size) {
244 memcpy(buffer, frame()->Buffer(), needed);
249 // TODO(fbarchard): Refactor into base class and share with lmi
250 size_t WebRtcVideoFrame::ConvertToRgbBuffer(uint32 to_fourcc, uint8* buffer,
251 size_t size, int stride_rgb) const {
252 if (!frame()->Buffer()) {
255 size_t width = frame()->Width();
256 size_t height = frame()->Height();
257 size_t needed = (stride_rgb >= 0 ? stride_rgb : -stride_rgb) * height;
259 LOG(LS_WARNING) << "RGB buffer is not large enough";
263 if (libyuv::ConvertFromI420(GetYPlane(), GetYPitch(), GetUPlane(),
264 GetUPitch(), GetVPlane(), GetVPitch(), buffer,
266 static_cast<int>(width),
267 static_cast<int>(height),
269 LOG(LS_WARNING) << "RGB type not supported: " << to_fourcc;
270 return 0; // 0 indicates error
275 void WebRtcVideoFrame::Attach(
276 RefCountedBuffer* video_buffer, size_t buffer_size, int w, int h,
277 size_t pixel_width, size_t pixel_height, int64 elapsed_time,
278 int64 time_stamp, int rotation) {
279 if (video_buffer_.get() == video_buffer) {
283 video_buffer_ = video_buffer;
284 frame()->SetWidth(w);
285 frame()->SetHeight(h);
286 pixel_width_ = pixel_width;
287 pixel_height_ = pixel_height;
288 elapsed_time_ = elapsed_time;
289 time_stamp_ = time_stamp;
290 rotation_ = rotation;
293 webrtc::VideoFrame* WebRtcVideoFrame::frame() {
294 return video_buffer_->frame();
297 const webrtc::VideoFrame* WebRtcVideoFrame::frame() const {
298 return video_buffer_->frame();
301 bool WebRtcVideoFrame::Reset(
302 uint32 format, int w, int h, int dw, int dh, uint8* sample,
303 size_t sample_size, size_t pixel_width, size_t pixel_height,
304 int64 elapsed_time, int64 time_stamp, int rotation) {
305 if (!Validate(format, w, h, sample, sample_size)) {
308 // Translate aliases to standard enums (e.g., IYUV -> I420).
309 format = CanonicalFourCC(format);
311 // Round display width and height down to multiple of 4, to avoid webrtc
312 // size calculation error on odd sizes.
313 // TODO(Ronghua): Remove this once the webrtc allocator is fixed.
314 dw = (dw > 4) ? (dw & ~3) : dw;
315 dh = (dh > 4) ? (dh & ~3) : dh;
317 // Set up a new buffer.
318 // TODO(fbarchard): Support lazy allocation.
321 if (rotation == 90 || rotation == 270) { // If rotated swap width, height.
326 size_t desired_size = SizeOf(new_width, new_height);
327 rtc::scoped_refptr<RefCountedBuffer> video_buffer(
328 new RefCountedBuffer(desired_size));
329 // Since the libyuv::ConvertToI420 will handle the rotation, so the
330 // new frame's rotation should always be 0.
331 Attach(video_buffer.get(), desired_size, new_width, new_height, pixel_width,
332 pixel_height, elapsed_time, time_stamp, 0);
334 int horiz_crop = ((w - dw) / 2) & ~1;
335 // ARGB on Windows has negative height.
336 // The sample's layout in memory is normal, so just correct crop.
337 int vert_crop = ((abs(h) - dh) / 2) & ~1;
338 // Conversion functions expect negative height to flip the image.
339 int idh = (h < 0) ? -dh : dh;
340 uint8* y = GetYPlane();
341 int y_stride = GetYPitch();
342 uint8* u = GetUPlane();
343 int u_stride = GetUPitch();
344 uint8* v = GetVPlane();
345 int v_stride = GetVPitch();
346 int r = libyuv::ConvertToI420(
347 sample, sample_size, y, y_stride, u, u_stride, v, v_stride, horiz_crop,
348 vert_crop, w, h, dw, idh, static_cast<libyuv::RotationMode>(rotation),
351 LOG(LS_ERROR) << "Error parsing format: " << GetFourccName(format)
352 << " return code : " << r;
358 VideoFrame* WebRtcVideoFrame::CreateEmptyFrame(
359 int w, int h, size_t pixel_width, size_t pixel_height, int64 elapsed_time,
360 int64 time_stamp) const {
361 WebRtcVideoFrame* frame = new WebRtcVideoFrame();
362 frame->InitToEmptyBuffer(w, h, pixel_width, pixel_height, elapsed_time,
367 void WebRtcVideoFrame::InitToEmptyBuffer(int w, int h, size_t pixel_width,
369 int64 elapsed_time, int64 time_stamp) {
370 size_t buffer_size = VideoFrame::SizeOf(w, h);
371 rtc::scoped_refptr<RefCountedBuffer> video_buffer(
372 new RefCountedBuffer(buffer_size));
373 Attach(video_buffer.get(), buffer_size, w, h, pixel_width, pixel_height,
374 elapsed_time, time_stamp, 0);
377 } // namespace cricket