/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/media/engine/webrtcvideoframe.h" #include "libyuv/convert.h" #include "webrtc/base/logging.h" #include "webrtc/media/base/videocapturer.h" #include "webrtc/media/base/videocommon.h" #include "webrtc/video_frame.h" using webrtc::kYPlane; using webrtc::kUPlane; using webrtc::kVPlane; namespace cricket { WebRtcVideoFrame::WebRtcVideoFrame() : timestamp_us_(0), rotation_(webrtc::kVideoRotation_0) {} WebRtcVideoFrame::WebRtcVideoFrame( const rtc::scoped_refptr& buffer, webrtc::VideoRotation rotation, int64_t timestamp_us) : video_frame_buffer_(buffer), timestamp_us_(timestamp_us), rotation_(rotation) {} WebRtcVideoFrame::WebRtcVideoFrame( const rtc::scoped_refptr& buffer, int64_t time_stamp_ns, webrtc::VideoRotation rotation) : WebRtcVideoFrame(buffer, rotation, time_stamp_ns / rtc::kNumNanosecsPerMicrosec) {} WebRtcVideoFrame::~WebRtcVideoFrame() {} bool WebRtcVideoFrame::Init(uint32_t format, int w, int h, int dw, int dh, uint8_t* sample, size_t sample_size, int64_t time_stamp_ns, webrtc::VideoRotation rotation) { return Reset(format, w, h, dw, dh, sample, sample_size, time_stamp_ns / rtc::kNumNanosecsPerMicrosec, rotation, true /*apply_rotation*/); } bool WebRtcVideoFrame::Init(const CapturedFrame* frame, int dw, int dh, bool apply_rotation) { return Reset(frame->fourcc, frame->width, frame->height, dw, dh, static_cast(frame->data), frame->data_size, frame->time_stamp / rtc::kNumNanosecsPerMicrosec, frame->rotation, apply_rotation); } int WebRtcVideoFrame::width() const { return video_frame_buffer_ ? video_frame_buffer_->width() : 0; } int WebRtcVideoFrame::height() const { return video_frame_buffer_ ? video_frame_buffer_->height() : 0; } const rtc::scoped_refptr& WebRtcVideoFrame::video_frame_buffer() const { return video_frame_buffer_; } VideoFrame* WebRtcVideoFrame::Copy() const { return new WebRtcVideoFrame(video_frame_buffer_, rotation_, timestamp_us_); } size_t WebRtcVideoFrame::ConvertToRgbBuffer(uint32_t to_fourcc, uint8_t* buffer, size_t size, int stride_rgb) const { RTC_CHECK(video_frame_buffer_); RTC_CHECK(video_frame_buffer_->native_handle() == nullptr); return VideoFrame::ConvertToRgbBuffer(to_fourcc, buffer, size, stride_rgb); } bool WebRtcVideoFrame::Reset(uint32_t format, int w, int h, int dw, int dh, uint8_t* sample, size_t sample_size, int64_t timestamp_us, webrtc::VideoRotation rotation, bool apply_rotation) { if (!Validate(format, w, h, sample, sample_size)) { return false; } // Translate aliases to standard enums (e.g., IYUV -> I420). format = CanonicalFourCC(format); // Set up a new buffer. // TODO(fbarchard): Support lazy allocation. int new_width = dw; int new_height = dh; // If rotated swap width, height. if (apply_rotation && (rotation == 90 || rotation == 270)) { new_width = dh; new_height = dw; } InitToEmptyBuffer(new_width, new_height); rotation_ = apply_rotation ? webrtc::kVideoRotation_0 : rotation; int horiz_crop = ((w - dw) / 2) & ~1; // ARGB on Windows has negative height. // The sample's layout in memory is normal, so just correct crop. int vert_crop = ((abs(h) - dh) / 2) & ~1; // Conversion functions expect negative height to flip the image. int idh = (h < 0) ? -dh : dh; int r = libyuv::ConvertToI420( sample, sample_size, video_frame_buffer_->MutableDataY(), video_frame_buffer_->StrideY(), video_frame_buffer_->MutableDataU(), video_frame_buffer_->StrideU(), video_frame_buffer_->MutableDataV(), video_frame_buffer_->StrideV(), horiz_crop, vert_crop, w, h, dw, idh, static_cast( apply_rotation ? rotation : webrtc::kVideoRotation_0), format); if (r) { LOG(LS_ERROR) << "Error parsing format: " << GetFourccName(format) << " return code : " << r; return false; } timestamp_us_ = timestamp_us; return true; } VideoFrame* WebRtcVideoFrame::CreateEmptyFrame(int w, int h, int64_t timestamp_us) const { WebRtcVideoFrame* frame = new WebRtcVideoFrame(); frame->InitToEmptyBuffer(w, h, rtc::kNumNanosecsPerMicrosec * timestamp_us); return frame; } void WebRtcVideoFrame::InitToEmptyBuffer(int w, int h) { video_frame_buffer_ = new rtc::RefCountedObject(w, h); rotation_ = webrtc::kVideoRotation_0; } void WebRtcVideoFrame::InitToEmptyBuffer(int w, int h, int64_t time_stamp_ns) { video_frame_buffer_ = new rtc::RefCountedObject(w, h); SetTimeStamp(time_stamp_ns); rotation_ = webrtc::kVideoRotation_0; } const VideoFrame* WebRtcVideoFrame::GetCopyWithRotationApplied() const { // If the frame is not rotated, the caller should reuse this frame instead of // making a redundant copy. if (rotation() == webrtc::kVideoRotation_0) { return this; } // If the video frame is backed up by a native handle, it resides in the GPU // memory which we can't rotate here. The assumption is that the renderers // which uses GPU to render should be able to rotate themselves. RTC_DCHECK(!video_frame_buffer()->native_handle()); if (rotated_frame_) { return rotated_frame_.get(); } int orig_width = width(); int orig_height = height(); int rotated_width = orig_width; int rotated_height = orig_height; if (rotation() == webrtc::kVideoRotation_90 || rotation() == webrtc::kVideoRotation_270) { rotated_width = orig_height; rotated_height = orig_width; } rotated_frame_.reset( CreateEmptyFrame(rotated_width, rotated_height, timestamp_us_)); // TODO(guoweis): Add a function in webrtc_libyuv.cc to convert from // VideoRotation to libyuv::RotationMode. int ret = libyuv::I420Rotate( video_frame_buffer_->DataY(), video_frame_buffer_->StrideY(), video_frame_buffer_->DataU(), video_frame_buffer_->StrideU(), video_frame_buffer_->DataV(), video_frame_buffer_->StrideV(), rotated_frame_->video_frame_buffer()->MutableDataY(), rotated_frame_->video_frame_buffer()->StrideY(), rotated_frame_->video_frame_buffer()->MutableDataU(), rotated_frame_->video_frame_buffer()->StrideU(), rotated_frame_->video_frame_buffer()->MutableDataV(), rotated_frame_->video_frame_buffer()->StrideV(), orig_width, orig_height, static_cast(rotation())); if (ret == 0) { return rotated_frame_.get(); } return nullptr; } } // namespace cricket