#include "base/memory/unsafe_shared_memory_region.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
#include "media/base/bitstream_buffer.h"
#include "media/base/tizen/logger/media_logger.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/gpu/tizen/tizen_video_encode_accelerator_helper.h"
#include "media/gpu/tizen/tizen_video_encode_accelerator_utils.h"
+#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/ozone/public/client_native_pixmap_factory_ozone.h"
namespace media {
namespace {
+#define CHECK_AND_RETURN_MEDIA_PACKET(function, return_type) \
+ do { \
+ auto error = static_cast<media_packet_error_e>(function); \
+ if (error != MEDIA_PACKET_ERROR_NONE) { \
+ TIZEN_MEDIA_LOG_NO_INSTANCE(ERROR) \
+ << #function << "() failed: `" << error << "`"; \
+ return return_type; \
+ } \
+ } while (0)
+
using EncoderState = TizenVideoEncodeAccelerator::EncoderState;
#define DECLARE_CASE_VALUE(param) \
constexpr const uint32_t kNumberOfBuffersPerLayer = 3;
+bool IsBufferCapableOfHoldingFormat(const HardwareBuffers& buffers,
+ VideoPixelFormat format,
+ gfx::Size frame_size) {
+ if (buffers.size() != VideoFrame::NumPlanes(format)) {
+ return false;
+ }
+
+ for (size_t plane = 0; plane < buffers.size(); ++plane) {
+ if (buffers[plane]->Size() <
+ static_cast<size_t>(
+ VideoFrame::PlaneSize(format, plane, frame_size).GetArea())) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace
+void TizenVideoEncodeAccelerator::ReturnBufferToPool(
+ HardwareBuffers buffers,
+ std::unique_ptr<gfx::TbmSurface>) {
+ temp_buffers_.push_back(std::move(buffers));
+}
+
+HardwareBuffers TizenVideoEncodeAccelerator::AllocateOrGetBufferFromPool(
+ VideoPixelFormat format,
+ gfx::Size frame_size) {
+ constexpr const size_t kMaxNumBuffers = 2;
+ if (temp_buffers_.empty() && allocated_buffers_ >= kMaxNumBuffers) {
+ return {};
+ }
+
+ if (!temp_buffers_.empty()) {
+ if (IsBufferCapableOfHoldingFormat(temp_buffers_.front(), format,
+ frame_size)) {
+ auto result = std::move(temp_buffers_.front());
+ temp_buffers_.pop_front();
+ return result;
+ } else {
+ temp_buffers_.pop_front();
+ allocated_buffers_ -= 1;
+ }
+ }
+
+ HardwareBuffers result;
+ for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); plane++) {
+ result.push_back(gfx::TizenGpuBuffer::Allocate(
+ VideoFrame::PlaneSize(format, plane, frame_size).GetArea(), true));
+ if (!result.back()) {
+ return {};
+ }
+ }
+ allocated_buffers_ += 1;
+ return result;
+}
+
+MediaPacketTuple TizenVideoEncodeAccelerator::CreateMediaPacketFromTBMSurface(
+ const scoped_refptr<VideoFrame>& video_frame,
+ const MediaFormatType& media_format) {
+ if (!video_frame->HasGpuMemoryBuffer())
+ return {};
+
+ const auto original_native_pixmap_handle =
+ video_frame->GetGpuMemoryBuffer()->CloneHandle().native_pixmap_handle;
+ constexpr uint64_t kFormatModifierLinear = 0;
+ // It supports only linear buffers, since it copies them and pass in special
+ // mode under TBM surface.
+ if (original_native_pixmap_handle.modifier != kFormatModifierLinear) {
+ return {};
+ }
+
+ auto buffers =
+ AllocateOrGetBufferFromPool(video_frame->format(), input_visible_size_);
+ if (buffers.empty()) {
+ TIZEN_MEDIA_LOG(ERROR) << "Failed to allocate temporary buffer";
+ return {};
+ }
+
+ gfx::NativePixmapHandle native_pixmap_handle;
+ for (size_t plane = 0; plane < VideoFrame::NumPlanes(video_frame->format());
+ plane++) {
+ const auto sample_size =
+ VideoFrame::SampleSize(video_frame->format(), plane);
+ const auto src_size_in_samples = VideoFrame::PlaneSizeInSamples(
+ video_frame->format(), plane, video_frame->visible_rect().size());
+ const auto bytes_per_element =
+ VideoFrame::BytesPerElement(video_frame->format(), plane);
+ const auto src_offset =
+ original_native_pixmap_handle.planes[plane].stride *
+ (video_frame->visible_rect().y() / sample_size.height()) +
+ (video_frame->visible_rect().x() / sample_size.height()) *
+ bytes_per_element;
+
+ const auto dst_plane_size_in_samples = VideoFrame::PlaneSizeInSamples(
+ video_frame->format(), plane, input_visible_size_);
+ const auto dst_stride = VideoFrame::RowBytes(plane, video_frame->format(),
+ input_visible_size_.width());
+
+ TRACE_EVENT0("gpu", "VideoEncoder.CopyPlane");
+ auto src_buffer = gfx::TizenGpuBuffer::ImportFromFd(
+ original_native_pixmap_handle.planes[plane].fd);
+ if (input_visible_size_ == video_frame->visible_rect().size()) {
+ buffers[plane]->CopyFrom(
+ *src_buffer, original_native_pixmap_handle.planes[plane].stride,
+ original_native_pixmap_handle.planes[plane].offset + src_offset,
+ dst_stride, 0, src_size_in_samples.width(),
+ src_size_in_samples.height(), bytes_per_element);
+ } else {
+ buffers[plane]->Scale(
+ *src_buffer, original_native_pixmap_handle.planes[plane].stride,
+ original_native_pixmap_handle.planes[plane].offset + src_offset,
+ dst_stride, 0, src_size_in_samples.width(),
+ src_size_in_samples.height(), dst_plane_size_in_samples.width(),
+ dst_plane_size_in_samples.height(), bytes_per_element);
+ }
+
+ native_pixmap_handle.planes.emplace_back(
+ dst_stride, 0,
+ VideoFrame::PlaneSize(video_frame->format(), plane, input_visible_size_)
+ .GetArea(),
+ buffers[plane]->ExportFd());
+ }
+
+ auto tbm_surface = gfx::TbmSurface::ImportTbmSurface(
+ native_pixmap_handle, video_frame->coded_size());
+
+ if (!tbm_surface)
+ return {};
+
+ media_packet_h media_packet_ptr{nullptr};
+
+ CHECK_AND_RETURN_MEDIA_PACKET(
+ media_packet_new_from_tbm_surface(
+ media_format.get(), **tbm_surface,
+ TizenVideoEncodeAcceleratorHelper::OnPacketProcessedByEncoderCallback,
+ this, &media_packet_ptr),
+ {});
+
+ return std::make_tuple(
+ MediaPacketType{media_packet_ptr},
+ base::ScopedClosureRunner{base::BindOnce(
+ &TizenVideoEncodeAccelerator::ReturnBufferToPool, weak_ptr_,
+ std::move(buffers), std::move(tbm_surface))});
+}
+
VideoEncodeAccelerator::SupportedProfiles
TizenVideoEncodeAccelerator::GetSupportedProfiles() {
TIZEN_MEDIA_LOG(INFO);
const size_t output_buffer_capacity = VideoFrame::AllocationSize(
config.input_format, config.input_visible_size);
+ input_visible_size_ = config.input_visible_size;
StartInternal();
CHECK_AND_RETURN_MEDIA_FORMAT(
media_format_set_video_mime(media_format.get(), MEDIA_FORMAT_NV12));
CHECK_AND_RETURN_MEDIA_FORMAT(media_format_set_video_width(
- media_format.get(), frame->visible_rect().width()));
+ media_format.get(), input_visible_size_.width()));
CHECK_AND_RETURN_MEDIA_FORMAT(media_format_set_video_height(
- media_format.get(), frame->visible_rect().height()));
+ media_format.get(), input_visible_size_.height()));
CHECK_AND_RETURN_MEDIA_FORMAT(media_format_set_video_frame_rate(
media_format.get(), kDefaultFramerate));
}
static const std::array media_packet_creation_functions{
- base::BindRepeating(CreateMediaPacketFromTBMSurface),
+ base::BindRepeating(
+ &TizenVideoEncodeAccelerator::CreateMediaPacketFromTBMSurface),
base::BindRepeating(CreateMediaPacketFromGpuMemoryBuffer),
base::BindRepeating(CreateMediaPacketFromRawData)};
for (const auto& media_packet_create_function :
media_packet_creation_functions) {
auto [media_packet, destruction_cb] =
- media_packet_create_function.Run(frame, media_format, this);
+ media_packet_create_function.Run(this, frame, media_format);
if (!media_packet)
continue;
#ifndef MEDIA_GPU_TIZEN_TIZEN_VIDEO_ENCODE_ACCELERATOR_H_
#define MEDIA_GPU_TIZEN_TIZEN_VIDEO_ENCODE_ACCELERATOR_H_
+#include <list>
#include <tuple>
#include <unordered_map>
#include <vector>
#include "media/video/video_encode_accelerator.h"
#include "ui/gfx/client_native_pixmap_factory.h"
#include "ui/gfx/range/range.h"
+#include "ui/gfx/tbm_surface.h"
+#include "ui/gfx/tizen_gpu_buffer.h"
namespace media {
+using HardwareBuffers = std::vector<std::unique_ptr<gfx::TizenGpuBuffer>>;
+
class TizenVideoEncodeAccelerator : public VideoEncodeAccelerator {
public:
TizenVideoEncodeAccelerator();
bool* is_idr_frame,
int* qp);
+ void ReturnBufferToPool(HardwareBuffers buffers,
+ std::unique_ptr<gfx::TbmSurface>);
+ HardwareBuffers AllocateOrGetBufferFromPool(VideoPixelFormat format,
+ gfx::Size frame_size);
+ MediaPacketTuple CreateMediaPacketFromTBMSurface(
+ const scoped_refptr<VideoFrame>& video_frame,
+ const MediaFormatType& media_format);
+
EncoderState encoder_state_{EncoderState::kUninitialized};
Client* client_{nullptr};
+ gfx::Size input_visible_size_;
+
// Task runner used for executing all methods of this class.
// Used so the callbacks from encoder can be dispatched to proper thread.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
frames_to_encode_;
std::vector<scoped_refptr<VideoFrame>> frames_with_data_in_encoder_;
+ std::list<HardwareBuffers> temp_buffers_;
+ size_t allocated_buffers_ = 0;
+
base::queue<BitstreamBuffer> buffers_;
absl::optional<Bitrate> last_set_bitrate_;
media_packet_unref(packet);
}
-MediaPacketTuple CreateMediaPacketFromTBMSurface(
+MediaPacketTuple CreateMediaPacketFromGpuMemoryBuffer(
+ TizenVideoEncodeAccelerator* thiz,
const scoped_refptr<VideoFrame>& video_frame,
- const MediaFormatType& media_format,
- TizenVideoEncodeAccelerator* thiz) {
+ const MediaFormatType& media_format) {
if (!video_frame->HasGpuMemoryBuffer())
return {};
- auto gpu_memory_buffer = video_frame->GetGpuMemoryBuffer()->CloneHandle();
-
- // Hardware encoder platform library does not apply offset given in TBM
- // surface. We cannot encode region indicated by visible rect, so we need
- // to fallback to other methods.
- if (video_frame->visible_rect().size() != video_frame->coded_size()) {
- TIZEN_MEDIA_LOG_NO_INSTANCE(VERBOSE)
- << "Visible rect size ("
- << video_frame->visible_rect().size().ToString()
- << ") is different than coded size "
- << video_frame->coded_size().ToString() << ")";
+ auto handle = video_frame->GetGpuMemoryBuffer()->CloneHandle();
+ constexpr uint64_t kFormatModifierLinear = 0;
+ // Linear buffers has it's dedicated function and they should not be handled
+ // in this function.
+ if (handle.native_pixmap_handle.modifier == kFormatModifierLinear) {
return {};
}
- for (const auto& plane : gpu_memory_buffer.native_pixmap_handle.planes) {
- if (plane.offset != 0) {
- TIZEN_MEDIA_LOG_NO_INSTANCE(VERBOSE) << "Offset is non-zero";
- return {};
- }
- }
-
- auto tbm_surface = gfx::TbmSurface::ImportTbmSurface(
- gpu_memory_buffer.native_pixmap_handle, video_frame->coded_size());
-
- if (!tbm_surface)
- return {};
-
- media_packet_h media_packet_ptr{nullptr};
-
- CHECK_AND_RETURN_MEDIA_PACKET(
- media_packet_new_from_tbm_surface(
- media_format.get(), **tbm_surface,
- TizenVideoEncodeAcceleratorHelper::OnPacketProcessedByEncoderCallback,
- thiz, &media_packet_ptr),
- {});
-
- return std::make_tuple(
- MediaPacketType{media_packet_ptr},
- base::ScopedClosureRunner{base::BindOnce(
- [](std::unique_ptr<gfx::TbmSurface>) {}, std::move(tbm_surface))});
-}
-
-MediaPacketTuple CreateMediaPacketFromGpuMemoryBuffer(
- const scoped_refptr<VideoFrame>& video_frame,
- const MediaFormatType& media_format,
- TizenVideoEncodeAccelerator* thiz) {
- if (!video_frame->HasGpuMemoryBuffer())
- return {};
-
// VideoFrame was recreated in Browser process after being passed from
// Renderer process. Its |gfx::GpuMemoryBuffer| received
// |VEA_READ_CAMERA_AND_CPU_READ_WRITE| buffer usage (see
// readonly purposes only. We can do it by changing |gfx::BufferUsage| to
// |SCANOUT_VEA_CPU_READ|.
auto gmb = gpu::GpuMemoryBufferImplNativePixmap::CreateFromHandle(
- thiz->GetClientNativePixmapFactory(),
- video_frame->GetGpuMemoryBuffer()->CloneHandle(),
+ thiz->GetClientNativePixmapFactory(), std::move(handle),
video_frame->coded_size(), gfx::BufferFormat::YUV_420_BIPLANAR,
gfx::BufferUsage::SCANOUT_VEA_CPU_READ, base::DoNothing());
}
MediaPacketTuple CreateMediaPacketFromRawData(
+ TizenVideoEncodeAccelerator* thiz,
const scoped_refptr<VideoFrame>& video_frame,
- const MediaFormatType& media_format,
- TizenVideoEncodeAccelerator* thiz) {
+ const MediaFormatType& media_format) {
// It is possible that this function will be called after failure of
// |CreateMediaPacketFromGpuMemoryBuffer|. As this function is not designed to
// work with |GpuMemoryBuffer|, we need to return here if |VideoFrame|
using MediaPacketTuple = std::tuple<MediaPacketType, base::ScopedClosureRunner>;
-MediaPacketTuple CreateMediaPacketFromTBMSurface(
- const scoped_refptr<VideoFrame>& video_frame,
- const MediaFormatType& media_format,
- TizenVideoEncodeAccelerator* thiz);
-
MediaPacketTuple CreateMediaPacketFromGpuMemoryBuffer(
+ TizenVideoEncodeAccelerator* thiz,
const scoped_refptr<VideoFrame>& video_frame,
- const MediaFormatType& media_format,
- TizenVideoEncodeAccelerator* thiz);
+ const MediaFormatType& media_format);
MediaPacketTuple CreateMediaPacketFromRawData(
+ TizenVideoEncodeAccelerator* thiz,
const scoped_refptr<VideoFrame>& video_frame,
- const MediaFormatType& media_format,
- TizenVideoEncodeAccelerator* thiz);
+ const MediaFormatType& media_format);
} // namespace media
return ret == 1;
}
+bool TizenGpuBuffer::CopyFrom(const TizenGpuBuffer& source,
+ int src_stride,
+ int src_offset,
+ int dst_stride,
+ int dst_offset,
+ int width,
+ int height,
+ size_t bytes_per_element) {
+ tbm_bo_handle src_handle = tbm_bo_get_handle(source.bo_.get(), TBM_DEVICE_2D);
+ tbm_bo_handle dst_handle = tbm_bo_get_handle(bo_.get(), TBM_DEVICE_2D);
+ GraphicsGABltRopInfo ga_info;
+ memset(&ga_info, 0, sizeof(GraphicsGABltRopInfo));
+
+ // src info
+ ga_info.src_handle = src_handle.u32;
+ ga_info.src_hbytesize = src_stride;
+ ga_info.src_rect.x = (src_offset % src_stride) / bytes_per_element;
+ ga_info.src_rect.y = src_offset / src_stride;
+ ga_info.src_rect.w = width;
+ ga_info.src_rect.h = height;
+
+ // dst info
+ ga_info.dst_handle = dst_handle.u32;
+ ga_info.dst_hbytesize = dst_stride;
+ ga_info.dst_rect.x = (dst_offset % dst_stride) / bytes_per_element;
+ ga_info.dst_rect.y = dst_offset / dst_stride;
+
+ // GA info
+ switch (bytes_per_element) {
+ case 1:
+ ga_info.color_format = GRAPHICS_GA_FORMAT_8BPP;
+ break;
+ case 2:
+ ga_info.color_format = GRAPHICS_GA_FORMAT_16BPP;
+ break;
+ default:
+ return false;
+ }
+ ga_info.rop_mode = GRAPHICS_GA_ROP_COPY;
+ ga_info.pre_alphamode = 0;
+ ga_info.ga_mode = GRAPHICS_GA_BITBLT_MODE_NORMAL;
+ ga_info.ga_op_type = GRAPHICS_GA_COPY;
+ int ret = Gfx_GA_BltRop(bufmgr_.get(), &ga_info);
+ return ret == 1;
+}
+
bool TizenGpuBuffer::Scale(uint32_t phys_addr,
int stride,
int dst_stride,
return Gfx_GA_Scale(bufmgr_.get(), &ga_info) == 1;
}
+bool TizenGpuBuffer::Scale(const TizenGpuBuffer& source,
+ int src_stride,
+ int src_offset,
+ int dst_stride,
+ int dst_offset,
+ int width,
+ int height,
+ int dst_width,
+ int dst_height,
+ size_t bytes_per_element) {
+ tbm_bo_handle src_handle = tbm_bo_get_handle(source.bo_.get(), TBM_DEVICE_2D);
+ tbm_bo_handle dst_handle = tbm_bo_get_handle(bo_.get(), TBM_DEVICE_2D);
+ GraphicsGAScaleInfo ga_info;
+ memset(&ga_info, 0, sizeof(GraphicsGAScaleInfo));
+
+ // src info
+ ga_info.src_handle = src_handle.u32;
+ ga_info.src_hbytesize = src_stride;
+ ga_info.src_rect.x = (src_offset % src_stride) / bytes_per_element;
+ ga_info.src_rect.y = src_offset / src_stride;
+ ga_info.src_rect.w = width;
+ ga_info.src_rect.h = height;
+
+ // dst info
+ ga_info.dst_handle = dst_handle.u32;
+ ga_info.dst_hbytesize = dst_stride;
+ ga_info.dst_rect.x = (dst_offset % dst_stride) / bytes_per_element;
+ ga_info.dst_rect.y = dst_offset / dst_stride;
+ ga_info.dst_rect.w = dst_width;
+ ga_info.dst_rect.h = dst_height;
+
+ // GA info
+ switch (bytes_per_element) {
+ case 1:
+ ga_info.color_format = GRAPHICS_GA_FORMAT_8BPP;
+ break;
+ case 2:
+ ga_info.color_format = GRAPHICS_GA_FORMAT_16BPP;
+ break;
+ default:
+ return false;
+ }
+ ga_info.rop_mode = GRAPHICS_GA_ROP_COPY;
+ ga_info.pre_alphamode = 0;
+ ga_info.ga_mode = GRAPHICS_GA_SCALE_MODE;
+ ga_info.ga_op_type = GRAPHICS_GA_SCALE;
+ return Gfx_GA_Scale(bufmgr_.get(), &ga_info) == 1;
+}
+
TizenGpuBuffer::TizenGpuBuffer(TbmBufMgrType bufmgr, TbmBOType bo)
: bufmgr_(std::move(bufmgr)), bo_(std::move(bo)) {}
int height,
int src_offset = 0,
int dst_offset = 0);
+ bool CopyFrom(const TizenGpuBuffer& source,
+ int src_stride,
+ int src_offset,
+ int dst_stride,
+ int dst_offset,
+ int width,
+ int height,
+ size_t bytes_per_element);
bool Scale(uint32_t phys_addr,
int stride,
int dst_stride,
int target_height,
int src_offset = 0,
int dst_offset = 0);
+ bool Scale(const TizenGpuBuffer& source,
+ int src_stride,
+ int src_offset,
+ int dst_stride,
+ int dst_offset,
+ int width,
+ int height,
+ int dst_width,
+ int dst_height,
+ size_t bytes_per_element);
const uint8_t* Memory() const { return data_; }
uint8_t* Memory() { return data_; }
size_t Size() const { return tbm_bo_size(bo_.get()); }