| // Copyright 2016 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "starboard/raspi/shared/dispmanx_util.h" |
| |
| #include <utility> |
| |
| #include "starboard/common/scoped_ptr.h" |
| #include "starboard/memory.h" |
| |
| namespace starboard { |
| namespace raspi { |
| namespace shared { |
| |
| namespace { |
| |
| const int kElementChangeAttributesFlagSrcRect = 1 << 3; |
| |
| class DispmanxAutoUpdate { |
| public: |
| DispmanxAutoUpdate() { |
| handle_ = vc_dispmanx_update_start(0 /*screen*/); |
| SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); |
| } |
| ~DispmanxAutoUpdate() { |
| if (handle_ != DISPMANX_NO_HANDLE) { |
| Update(); |
| } |
| } |
| |
| DISPMANX_UPDATE_HANDLE_T handle() const { return handle_; } |
| |
| void Update() { |
| SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); |
| int32_t result = vc_dispmanx_update_submit_sync(handle_); |
| SB_DCHECK(result == 0) << " result=" << result; |
| handle_ = DISPMANX_NO_HANDLE; |
| } |
| |
| private: |
| DISPMANX_UPDATE_HANDLE_T handle_; |
| |
| DispmanxAutoUpdate(const DispmanxAutoUpdate&) = delete; |
| void operator=(const DispmanxAutoUpdate&) = delete; |
| }; |
| |
| } // namespace |
| |
| DispmanxResource::DispmanxResource(VC_IMAGE_TYPE_T image_type, |
| uint32_t width, |
| uint32_t height, |
| uint32_t visible_width, |
| uint32_t visible_height) |
| : width_(width), |
| height_(height), |
| visible_width_(visible_width), |
| visible_height_(visible_height) { |
| static const uint32_t kMaxDimension = 1 << 16; |
| |
| SB_DCHECK(width_ > 0 && width_ < kMaxDimension); |
| SB_DCHECK(height_ > 0 && height_ < kMaxDimension); |
| SB_DCHECK(visible_width_ > 0 && visible_width_ < kMaxDimension); |
| SB_DCHECK(visible_height > 0 && visible_height < kMaxDimension); |
| SB_DCHECK(width_ >= visible_width_); |
| SB_DCHECK(height_ >= visible_height); |
| |
| uint32_t vc_image_ptr; |
| |
| handle_ = vc_dispmanx_resource_create( |
| image_type, visible_width_ | (width_ << 16), |
| visible_height | (height_ << 16), &vc_image_ptr); |
| SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); |
| } |
| |
| void DispmanxYUV420Resource::WriteData(const void* data) { |
| SB_DCHECK(handle() != DISPMANX_NO_HANDLE); |
| |
| DispmanxRect dst_rect(0, 0, width(), height() * 3 / 2); |
| int32_t result = vc_dispmanx_resource_write_data( |
| handle(), VC_IMAGE_YUV420, width(), const_cast<void*>(data), &dst_rect); |
| SB_DCHECK(result == 0); |
| } |
| |
| void DispmanxYUV420Resource::ClearWithBlack() { |
| scoped_array<uint8_t> data(new uint8_t[width() * height() * 3 / 2]); |
| memset(data.get(), 0, width() * height()); |
| memset(data.get() + width() * height(), 0x80, width() * height() / 2); |
| WriteData(data.get()); |
| } |
| |
| void DispmanxRGB565Resource::WriteData(const void* data) { |
| SB_DCHECK(handle() != DISPMANX_NO_HANDLE); |
| |
| DispmanxRect dst_rect(0, 0, width(), height()); |
| int32_t result = |
| vc_dispmanx_resource_write_data(handle(), VC_IMAGE_RGB565, width() * 2, |
| const_cast<void*>(data), &dst_rect); |
| SB_DCHECK(result == 0); |
| } |
| |
| void DispmanxRGB565Resource::ClearWithBlack() { |
| scoped_array<uint8_t> data(new uint8_t[width() * height() * 2]); |
| memset(data.get(), 0, width() * height() * 2); |
| WriteData(data.get()); |
| } |
| |
| DispmanxElement::DispmanxElement(const DispmanxDisplay& display, |
| int32_t layer, |
| const DispmanxRect& dest_rect, |
| const DispmanxResource& src, |
| const DispmanxRect& src_rect) { |
| DispmanxAutoUpdate update; |
| handle_ = vc_dispmanx_element_add(update.handle(), display.handle(), layer, |
| &dest_rect, src.handle(), &src_rect, |
| DISPMANX_PROTECTION_NONE, NULL /*alpha*/, |
| NULL /*clamp*/, DISPMANX_NO_ROTATE); |
| SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); |
| } |
| |
| DispmanxElement::~DispmanxElement() { |
| DispmanxAutoUpdate update; |
| int32_t result = vc_dispmanx_element_remove(update.handle(), handle_); |
| SB_DCHECK(result == 0) << " result=" << result; |
| } |
| |
| void DispmanxElement::ChangeSource(const DispmanxResource& new_src) { |
| DispmanxAutoUpdate update; |
| vc_dispmanx_element_change_source(update.handle(), handle_, new_src.handle()); |
| } |
| |
| DispmanxVideoRenderer::DispmanxVideoRenderer(const DispmanxDisplay& display, |
| int32_t layer) |
| : black_frame_(256, 256, 256, 256) { |
| black_frame_.ClearWithBlack(); |
| element_.reset(new DispmanxElement(display, layer, DispmanxRect(), |
| black_frame_, DispmanxRect())); |
| } |
| |
| void DispmanxVideoRenderer::Update( |
| const scoped_refptr<VideoFrame>& video_frame) { |
| SB_DCHECK(video_frame); |
| |
| if (frame_ == video_frame) { |
| return; |
| } |
| |
| if (video_frame->is_end_of_stream()) { |
| element_->ChangeSource(black_frame_); |
| frame_ = video_frame; |
| return; |
| } |
| |
| DispmanxYUV420Resource* resource = |
| static_cast<DispmanxVideoFrame*>(video_frame.get())->resource(); |
| element_->ChangeSource(*resource); |
| frame_ = video_frame; |
| } |
| |
| void DispmanxVideoRenderer::HideElement() { |
| DispmanxResource transparent_resource_; |
| element_->ChangeSource(transparent_resource_); |
| |
| // |frame| != |hidden_frame_| ensures that the call to Update() in |
| // ShowElement() actually updates the video renderer with the desired video |
| // frame. |
| hidden_frame_ = std::move(frame_); |
| } |
| |
| void DispmanxVideoRenderer::ShowElement() { |
| Update(hidden_frame_); |
| } |
| |
| } // namespace shared |
| } // namespace raspi |
| } // namespace starboard |