blob: 076c1a5ff0753c0d07677432a367ebb8c95f1022 [file] [log] [blame]
// 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 "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]);
SbMemorySet(data.get(), width() * height(), 0);
SbMemorySet(data.get() + width() * height(), width() * height() / 2, 0x80);
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]);
SbMemorySet(data.get(), width() * height() * 2, 0);
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