| // Copyright 2017 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/linux/shared/decode_target_internal.h" |
| |
| #if SB_HAS(GLES2) |
| #include <EGL/egl.h> |
| #include <GLES2/gl2.h> |
| #include <GLES2/gl2ext.h> |
| |
| #include "starboard/shared/gles/gl_call.h" |
| #endif |
| |
| #include "starboard/decode_target.h" |
| |
| SbDecodeTargetPrivate::Data::~Data() { |
| #if SB_HAS(GLES2) |
| glDeleteTextures(1, &info.planes[0].texture); |
| SB_DCHECK(glGetError() == GL_NO_ERROR); |
| #endif |
| } |
| |
| namespace starboard { |
| namespace shared { |
| |
| namespace { |
| |
| using starboard::player::filter::CpuVideoFrame; |
| |
| struct CreateParams { |
| SbDecodeTarget decode_target_out; |
| scoped_refptr<CpuVideoFrame> frame; |
| }; |
| |
| #if SB_HAS(GLES2) |
| void CreateWithContextRunner(void* context) { |
| CreateParams* params = static_cast<CreateParams*>(context); |
| |
| SB_DCHECK(params->frame); |
| SB_DCHECK(params->frame->format() == CpuVideoFrame::kYV12); |
| static const SbDecodeTargetFormat format = kSbDecodeTargetFormat3PlaneYUVI420; |
| static const int plane_count = 3; |
| |
| if (!SbDecodeTargetIsValid(params->decode_target_out)) { |
| params->decode_target_out = new SbDecodeTargetPrivate; |
| params->decode_target_out->data = new SbDecodeTargetPrivate::Data; |
| params->decode_target_out->data->info.format = format; |
| |
| GLuint textures[3]; |
| GL_CALL(glGenTextures(plane_count, textures)); |
| for (int plane_index = 0; plane_index < plane_count; plane_index++) { |
| params->decode_target_out->data->info.is_opaque = true; |
| |
| SbDecodeTargetInfoPlane& plane = |
| params->decode_target_out->data->info.planes[plane_index]; |
| plane.texture = textures[plane_index]; |
| plane.gl_texture_target = GL_TEXTURE_2D; |
| plane.content_region.left = 0.0f; |
| plane.content_region.top = 0.0f; |
| plane.width = 0; |
| plane.height = 0; |
| } |
| } |
| |
| SbDecodeTargetInfo& target_info = params->decode_target_out->data->info; |
| |
| for (int plane_index = 0; plane_index < plane_count; plane_index++) { |
| const CpuVideoFrame::Plane& video_frame_plane = |
| params->frame->GetPlane(plane_index); |
| |
| GL_CALL(glBindTexture( |
| GL_TEXTURE_2D, |
| params->decode_target_out->data->info.planes[plane_index].texture)); |
| GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, |
| video_frame_plane.pitch_in_bytes)); |
| if (target_info.planes[plane_index].width == video_frame_plane.width && |
| target_info.planes[plane_index].height == video_frame_plane.height) { |
| // No need to reallocate texture object, only update pixels. |
| GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_frame_plane.width, |
| video_frame_plane.height, GL_ALPHA, |
| GL_UNSIGNED_BYTE, video_frame_plane.data)); |
| |
| } else { |
| // As part of texture initialization, explicitly specify all parameters. |
| GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); |
| GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); |
| GL_CALL( |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); |
| GL_CALL( |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); |
| |
| GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, video_frame_plane.width, |
| video_frame_plane.height, 0, GL_ALPHA, |
| GL_UNSIGNED_BYTE, video_frame_plane.data)); |
| } |
| |
| GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); |
| GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); |
| |
| // Set sizes and regions. |
| target_info.planes[plane_index].content_region.right = |
| static_cast<float>(video_frame_plane.width); |
| target_info.planes[plane_index].content_region.bottom = |
| static_cast<float>(video_frame_plane.height); |
| target_info.planes[plane_index].width = video_frame_plane.width; |
| target_info.planes[plane_index].height = video_frame_plane.height; |
| } |
| |
| target_info.width = params->frame->width(); |
| target_info.height = params->frame->height(); |
| } |
| #endif |
| |
| } // namespace |
| |
| SbDecodeTarget DecodeTargetCreate( |
| SbDecodeTargetGraphicsContextProvider* provider, |
| scoped_refptr<CpuVideoFrame> frame, |
| SbDecodeTarget decode_target) { |
| CreateParams params; |
| params.decode_target_out = decode_target; |
| params.frame = frame; |
| |
| #if SB_HAS(GLES2) |
| if (!provider) { |
| if (SbDecodeTargetIsValid(params.decode_target_out)) { |
| // Should the decode target have been created and the GLES context been |
| // somehow lost, it is released without the context |
| SbDecodeTargetRelease(params.decode_target_out); |
| } |
| params.decode_target_out = kSbDecodeTargetInvalid; |
| } else { |
| SbDecodeTargetRunInGlesContext(provider, &CreateWithContextRunner, ¶ms); |
| } |
| #endif |
| |
| return params.decode_target_out; |
| } |
| |
| void DecodeTargetRelease(SbDecodeTargetGraphicsContextProvider* |
| decode_target_graphics_context_provider, |
| SbDecodeTarget decode_target) { |
| #if SB_HAS(GLES2) |
| SbDecodeTargetReleaseInGlesContext(decode_target_graphics_context_provider, |
| decode_target); |
| #endif |
| } |
| |
| SbDecodeTarget DecodeTargetCopy(SbDecodeTarget decode_target) { |
| SbDecodeTarget out_decode_target = new SbDecodeTargetPrivate; |
| out_decode_target->data = decode_target->data; |
| |
| return out_decode_target; |
| } |
| |
| } // namespace shared |
| } // namespace starboard |