fixup!fixup![M108 Aura Migration][MM][WebRtc] New implementation for local stream renderer
https://review.tizen.org/gerrit/#/c/293279/
Fixup! [M108 Migration] Support camera to canvas feature
https://review.tizen.org/gerrit/#/c/292855/
Optimize video hole frame updates
https://review.tizen.org/gerrit/#/c/291925/
fixup! [VD][MM] Get H264 max_res from RS on M108
https://review.tizen.org/gerrit/#/c/301521/
[MM] Do not perform operation by buffering state during in low latency mode.
https://review.tizen.org/gerrit/#/c/301932/
Change-Id: I4046fb2ebee17c9305f1d7c38e21841398bce2b4
Signed-off-by: peng.yin <peng8.yin@samsung.com>
}
handle.width = width;
handle.height = height;
+ handle.tbm_destroy_type = buffer_handle.tbm_destroy_type;
params->gpu_memory_buffer = std::move(handle);
command_buffer_->CreateImage(std::move(params));
scoped_refptr<viz::ContextProvider> context_provider) {
context_provider_ = context_provider;
}
+ void SetTbmDestroyType(gfx::TbmDestroyType type) {
+ buffer_handle_.tbm_destroy_type = type;
+ }
#endif
// Creates a frame which indicates end-of-stream.
VideoColorSpace color_space_info;
gfx.mojom.HDRMetadata? hdr_metadata;
bool is_rtc;
+ [EnableIf=tizen_multimedia]
+ gfx.mojom.Size max_coded_size;
[EnableIf=is_tizen_tv]
string hdr_info;
[EnableIf=is_tizen_tv]
output->set_framerate_den(input.framerate_den());
#endif
+#if defined(TIZEN_MULTIMEDIA)
+ gfx::Size max_coded_size;
+ input.ReadMaxCodedSize(&max_coded_size);
+ output->set_max_coded_size(max_coded_size);
+#endif
+
if (!output->IsValidConfig())
return false;
}
#endif
+#if defined(TIZEN_MULTIMEDIA)
+ static const gfx::Size& max_coded_size(
+ const media::VideoDecoderConfig& input) {
+ return input.max_coded_size();
+ }
+#endif
+
static bool Read(media::mojom::VideoDecoderConfigDataView input,
media::VideoDecoderConfig* output);
};
return true;
}
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_TBM_SUPPORT)
+void WebMediaPlayerMS::PaintDecodedVideoFrame(
+ scoped_refptr<media::VideoFrame> decoded_frame) {
+ base::AutoLock auto_lock(request_frame_lock_);
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // update decoded frame
+ decoded_video_frame_ = decoded_frame;
+ request_frame_waiter_.Signal();
+}
+#endif
+
void WebMediaPlayerMS::Paint(cc::PaintCanvas* canvas,
const gfx::Rect& rect,
cc::PaintFlags& flags) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_TBM_SUPPORT) && \
+ defined(TIZEN_MULTIMEDIA)
+ LOG(INFO) << __func__ << ", rect " << rect.ToString();
+ scoped_refptr<media::VideoFrame> decoded_frame =
+ compositor_->GetCurrentFrame();
+
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kEnableFrameRawDataCopy) &&
+ (!decoded_frame ||
+ decoded_frame->storage_type() == media::VideoFrame::STORAGE_HOLE)) {
+ frame_deliverer_->RequestVideoDecodedBuffer();
+ for (int i = 0; i < 3; ++i) {
+ SendVideoDecodedBufferRequest();
+ // need to wait some time to ensure decoded frame have return.
+ bool signaled =
+ request_frame_waiter_.TimedWait( // FIXME: will block UI thread
+ base::Milliseconds(22)); // wait max 22ms
+ if (signaled) {
+ break;
+ }
+ }
+ base::AutoLock auto_lock(request_frame_lock_);
+ decoded_frame = decoded_video_frame_;
+
+ // Prevent to paint a black screen when a decoded frame is not prepared.
+ if (!decoded_frame)
+ return;
+ }
+
+ const scoped_refptr<media::VideoFrame> frame = decoded_frame;
+#else
const scoped_refptr<media::VideoFrame> frame = compositor_->GetCurrentFrame();
+#endif
scoped_refptr<viz::RasterContextProvider> provider;
if (frame && frame->HasTextures()) {
const gfx::RectF dest_rect(rect);
video_renderer_.Paint(frame, canvas, dest_rect, flags,
GetFrameTransformation(frame), provider.get());
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_TBM_SUPPORT)
+ // Do not keep decoded video frame resource here,
+ // renderer will paint black rectangle if no new decoded frame
+ decoded_video_frame_ = nullptr;
+#endif
}
scoped_refptr<media::VideoFrame> WebMediaPlayerMS::GetCurrentFrameThenUpdate() {
scoped_refptr<media::VideoFrame> frame,
media::PaintCanvasVideoRenderer* video_renderer) {
scoped_refptr<media::VideoFrame> new_frame;
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE)
+ if (frame->storage_type() == media::VideoFrame::STORAGE_HOLE) {
+ new_frame = media::VideoFrame::CreateHoleFrame(frame->natural_size());
+ } else if (frame->HasTextures()) {
+#else
if (frame->HasTextures()) {
+#endif
DCHECK(frame->format() == media::PIXEL_FORMAT_ARGB ||
frame->format() == media::PIXEL_FORMAT_XRGB ||
frame->format() == media::PIXEL_FORMAT_ABGR ||
video_frame_provider_client_->StopUsingProvider();
video_frame_provider_client_ = client;
+#if defined(TIZEN_VIDEO_HOLE)
+ video_frame_provider_client_changed_ = true;
+#endif
if (video_frame_provider_client_ && !stopped_)
video_frame_provider_client_->StartRendering();
}
return;
}
+#if defined(TIZEN_VIDEO_HOLE)
+ // Turn off rendering algorithm for pure video-hole frames
+ if (frame->storage_type() == media::VideoFrame::STORAGE_HOLE) {
+ rendering_frame_buffer_.reset();
+ RenderWithoutAlgorithm(std::move(frame), is_copy);
+ return;
+ }
+#endif
+
// This is a signal frame saying that the stream is stopped.
if (frame->metadata().end_of_stream) {
rendering_frame_buffer_.reset();
scoped_refptr<media::VideoFrame> frame,
bool is_copy) {
DCHECK(video_frame_compositor_task_runner_->BelongsToCurrentThread());
+ bool frame_size_changed = true;
+ bool is_video_hole = false;
+ bool provider_changed = true;
+#if defined(TIZEN_VIDEO_HOLE)
+ is_video_hole = frame->storage_type() == media::VideoFrame::STORAGE_HOLE;
+ provider_changed = video_frame_provider_client_changed_;
+#endif
+
{
base::AutoLock auto_lock(current_frame_lock_);
// Last timestamp in the stream might not have timestamp.
frame->timestamp() > current_frame_->timestamp()) {
last_render_length_ = frame->timestamp() - current_frame_->timestamp();
}
- SetCurrentFrame(std::move(frame), is_copy, absl::nullopt);
+ frame_size_changed =
+ SetCurrentFrame(std::move(frame), is_copy, absl::nullopt);
}
- if (video_frame_provider_client_)
+
+ if (is_video_hole && !frame_size_changed && !provider_changed)
+ return;
+
+ if (video_frame_provider_client_) {
video_frame_provider_client_->DidReceiveFrame();
+ }
+#if defined(TIZEN_VIDEO_HOLE)
+ video_frame_provider_client_changed_ = false;
+#endif
}
-void WebMediaPlayerMSCompositor::SetCurrentFrame(
+bool WebMediaPlayerMSCompositor::SetCurrentFrame(
scoped_refptr<media::VideoFrame> frame,
bool is_copy,
absl::optional<base::TimeTicks> expected_display_time) {
if (!current_frame_rendered_)
++dropped_frame_count_;
+ bool previous_frame_rendered = current_frame_rendered_;
current_frame_rendered_ = false;
// Compare current frame with |frame|. Initialize values as if there is no
// current frame.
bool is_first_frame = true;
bool has_frame_size_changed = false;
+ bool frame_content_did_not_change = false;
absl::optional<media::VideoTransformation> new_transform =
media::kNoTransformation;
if (*new_opacity == media::IsOpaque(current_frame_->format()))
new_opacity.reset();
+#if defined(TIZEN_VIDEO_HOLE)
+ if (frame->storage_type() == media::VideoFrame::STORAGE_HOLE &&
+ current_frame_->storage_type() == media::VideoFrame::STORAGE_HOLE &&
+ !has_frame_size_changed) {
+ frame_content_did_not_change = true;
+ }
+#endif
}
+ if (frame_content_did_not_change)
+ current_frame_rendered_ = previous_frame_rendered;
current_frame_ = std::move(frame);
current_frame_is_copy_ = is_copy;
SetMetadata();
CrossThreadBindOnce(&WebMediaPlayerMSCompositor::CheckForFrameChanges,
weak_this_, is_first_frame, has_frame_size_changed,
std::move(new_transform), std::move(new_opacity)));
+ return has_frame_size_changed || is_first_frame;
}
void WebMediaPlayerMSCompositor::CheckForFrameChanges(
bool is_copy);
// Update |current_frame_| and |dropped_frame_count_|
- void SetCurrentFrame(
+ bool SetCurrentFrame(
scoped_refptr<media::VideoFrame> frame,
bool is_copy,
absl::optional<base::TimeTicks> expected_presentation_time);
raw_ptr<cc::VideoFrameProvider::Client, DanglingUntriaged>
video_frame_provider_client_;
+#if defined(TIZEN_VIDEO_HOLE)
+ bool video_frame_provider_client_changed_{false};
+#endif
+
// |current_frame_| is updated only on compositor thread. The object it
// holds can be freed on the compositor thread if it is the last to hold a
// reference but media::VideoFrame is a thread-safe ref-pointer. It is
LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
<< " state:" << GetString(GetPlayerState());
- if (!SetBufferType())
+ if (!SetBufferType(VideoDecoderConfig{}))
return;
sink_ = sink;
int MediaPlayerESPlusPlayer::SetVideoStreamInfo(
const media::VideoDecoderConfig& video_config,
esplusplayer_video_stream_info& video_stream_info) {
- video_stream_info.width = video_config.coded_size().width();
- video_stream_info.height = video_config.coded_size().height();
+ if (!SetBufferType(video_config)) {
+ LOG_ID(ERROR, player_id_) << "SetBufferType failed.";
+ return ESPLUSPLAYER_ERROR_TYPE_UNKNOWN;
+ }
+
video_stream_info.mime_type =
ConvertToESPlusVideoMimeType(video_config.codec());
} else {
video_stream_info.codec_data = nullptr;
}
- auto max_resolution =
+ auto ri_max_resolution =
GetMaxResolution(video_stream_info.mime_type, is_video_hole_);
- LOG(INFO) << "max resolution: " << max_resolution.ToString();
-
- video_stream_info.max_width = max_resolution.width();
- video_stream_info.max_height = max_resolution.height();
+ // |video_resolution| is the current video size, |video_max_resolution|
+ // is the potential max video size, it to say that the video source
+ // size may be changed to that size in playback state dynamically.
+ // for every video playback scenarios, the caller should set it to a
+ // suitable value to |video_max_resolution| to avoid decoder resource
+ // waste or resource conflicts.
+ auto video_max_resolution = video_config.max_coded_size();
+ auto video_resolution = video_config.coded_size();
+ LOG_ID(INFO, player_id_) << "Available max resolution from resource manager:"
+ << ri_max_resolution.ToString()
+ << " video max resolution:"
+ << video_max_resolution.ToString()
+ << " video resolution:"
+ << video_resolution.ToString();
+
+ // Correct the video max resolution if it is smaller than video resolution.
+ video_max_resolution.SetToMax(video_resolution);
+
+ // Constrain the video resolution within the ri max resolution.
+ video_resolution.SetToMin(ri_max_resolution);
+
+ // Constrain the video max resolution within the ri max resolution.
+ video_max_resolution.SetToMin(ri_max_resolution);
+
+ video_stream_info.max_width = video_max_resolution.width();
+ video_stream_info.max_height = video_max_resolution.height();
+ video_stream_info.width = video_resolution.width();
+ video_stream_info.height = video_resolution.height();
return ESPLUSPLAYER_ERROR_TYPE_NONE;
}
}
void MediaPlayerESPlusPlayer::PerformOperationForData() {
+ // The low latency mode doesn't buffer packets. It decodes and renders
+ // a packet at once. WebRtc uses this feature.
+ if (is_video_low_latency_)
+ return;
+
if (is_seeking_ || is_preparing_)
return;
void SetShouldFeed(DemuxerStream::Type type, bool value);
bool ReadRequested(DemuxerStream::Type type) const;
void ReadBuffer(DemuxerStream::Type type);
- virtual bool SetBufferType() = 0;
+ virtual bool SetBufferType(const media::VideoDecoderConfig&) = 0;
#if defined(TIZEN_VIDEO_HOLE)
void PrepareVideoHole() override;
#endif
bool is_video_hole_ = false;
+ bool is_video_low_latency_ = false;
int player_id_ = 0;
// first -> pts, second -> duration
base::StaticMap<DemuxerStream::Type,
LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
}
-bool MediaPlayerESPlusPlayerCommon::SetBufferType() {
+bool MediaPlayerESPlusPlayerCommon::SetBufferType(
+ const media::VideoDecoderConfig&) {
if (GetPlayerState() != ESPLUSPLAYER_STATE_IDLE)
return true;
MediaPlayerESPlusPlayerCommon();
~MediaPlayerESPlusPlayerCommon() override;
- bool SetBufferType() override;
+ bool SetBufferType(const media::VideoDecoderConfig&) override;
void ToggleFullscreenMode(bool is_fullscreen) override;
#if defined(TIZEN_VIDEO_HOLE)
void PrepareVideoHole() override;
return total_buffer_size - kPlayerAudioBufferSize;
}
-bool MediaPlayerESPlusPlayerTV::SetBufferType() {
- esplusplayer_decoded_video_frame_buffer_type video_frame_buffer_type =
- is_video_hole_ ? ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_NONE
- : ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_COPY;
+void MediaPlayerESPlusPlayerTV::SetTbmCallbackPolicy(
+ const media::VideoDecoderConfig& video_config) {
+ auto policy = ESPLUSPLAYER_RSC_ALLOC_EXCLUSIVE;
+#if TIZEN_VERSION_AT_LEAST(8, 0, 0)
+ // Although we set the decoder config to fit the spec of mfc decoder in
+ // hopes of getting its priority, some other tizen apps (e.g. live TV) may
+ // still hold the mfc even they go into background. so it requires us to set
+ // proper RM policy to force ESPP to acquire a mfc decoder ownership from
+ // these apps.
+ policy = ESPLUSPLAYER_RSC_ALLOC_EXCLUSIVE_MFC_FORCED;
+#endif // TIZEN_VERSION_AT_LEAST(8, 0, 0)
+
+#if defined(TIZEN_MULTIMEDIA_MJPEG_SUPPORT)
+ if (video_config.codec() == VideoCodec::kMJPEG) {
+ policy = ESPLUSPLAYER_RSC_ALLOC_EXCLUSIVE;
+ }
+#endif // defined(TIZEN_MULTIMEDIA_MJPEG_SUPPORT)
+ LOG_ID(INFO, player_id_) << " Set RM policy for Tbm:"
+ << static_cast<int>(policy);
+
+ auto error = esplusplayer_set_resource_allocate_policy(esplayer_, policy);
+ if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+ LOG_ID(ERROR, player_id_)
+ << "esplusplayer_set_resource_allocate_policy failed. error #"
+ << esplusplayer_get_error_string(
+ static_cast<esplusplayer_error_type>(error));
+ }
+}
- int error = esplusplayer_set_video_frame_buffer_type(esplayer_,
- video_frame_buffer_type);
+bool MediaPlayerESPlusPlayerTV::SetBufferType(
+ const media::VideoDecoderConfig& video_config) {
+ if (!video_config.IsValidConfig()) {
+ LOG_ID(INFO, player_id_) << "Invalid video config.";
+ return false;
+ }
+
+ if (enable_tbm_buffer_callback_) {
+ LOG_ID(INFO, player_id_) << "Recall EnableTbmBufferCallback.";
+ SetTbmCallbackPolicy(video_config);
+ EnableTbmBufferCallback(true);
+ return true;
+ }
+
+ if (!is_video_hole_) {
+ decoded_frame_buffer_type_ =
+ ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_COPY;
+ }
+
+#if defined(TIZEN_VIDEO_HOLE) && defined(TIZEN_TBM_SUPPORT) && \
+ TIZEN_VERSION_AT_LEAST(6, 5, 0)
+ bool enable_frame_raw_data_copy =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableFrameRawDataCopy);
+ LOG_ID(INFO, player_id_) << "(" << static_cast<void*>(this) << ") "
+ << __func__ << ", kEnableFrameRawDataCopy: "
+ << enable_frame_raw_data_copy;
+
+ if (video_config.enable_manual_copy()) {
+ // `enable_manual_copy` means there is a potential requirement to enable
+ // TBM decoded data return, but this function is only available on mfc &
+ // mjpeg decoders.
+ SetTbmCallbackPolicy(video_config);
+
+ if (is_video_hole_ && !is_video_drm_eme_ && enable_frame_raw_data_copy) {
+ // should ensure App can get frame buffer by canvas2d API drawImage and
+ // getImageData
+ decoded_frame_buffer_type_ =
+ ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_MANUAL_COPY;
+ }
+ }
+#endif
+
+ int error = esplusplayer_set_video_frame_buffer_type(
+ esplayer_, decoded_frame_buffer_type_);
if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
- LOG(ERROR) << "esplusplayer_set_video_frame_buffer_type failed. error #"
- << esplusplayer_get_error_string(
- static_cast<esplusplayer_error_type>(error));
+ LOG_ID(ERROR, player_id_)
+ << "esplusplayer_set_video_frame_buffer_type failed. error #"
+ << esplusplayer_get_error_string(
+ static_cast<esplusplayer_error_type>(error));
return false;
}
DemuxerStream::Type type,
scoped_refptr<DecoderBuffer> buffer) override;
void InitializeStreamConfig(DemuxerStream::Type type) override;
- bool SetBufferType() override;
+ bool SetBufferType(const media::VideoDecoderConfig& video_config) override;
player_buffer_size_t GetMaxVideoBufferSize(
const media::VideoDecoderConfig& video_config) override;
+ void SetTbmCallbackPolicy(const media::VideoDecoderConfig& video_config);
private:
#if TIZEN_VERSION_AT_LEAST(6, 0, 0)
bool is_hdr_changed_{false};
esplusplayer_matroska_color matroska_color_;
- bool is_video_low_latency_{false};
esplusplayer_decoded_video_frame_buffer_type decoded_frame_buffer_type_{
ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_NONE};
bool enable_tbm_buffer_callback_{false};
#endif
namespace gfx {
+#if defined(TIZEN_TBM_SUPPORT)
+// There are 2 flows could trigger TBM destroy: GLImageEGL::~GLImageEGL()
+// and TizenEsPlusPlayerRenderer::ReleaseTbmBuffer;
+// Define a type to specify how to destroy TBM.
+enum TbmDestroyType {
+ // triggered by GLImageEGL::~GLImageEGL()
+ kDestroyTbmByEgl = 0,
+ // triggered by video frame DestructionObserver
+ kDestroyTbmByVideoFrame = 1,
+};
+#endif
struct GFX_EXPORT TbmBufferHandle {
TbmBufferHandle() {}
#if defined(TIZEN_TBM_SUPPORT)
size_t tbm_surface{0};
size_t media_packet{0};
+ TbmDestroyType tbm_destroy_type = kDestroyTbmByEgl;
int32_t player_id = 0;
int32_t key_num = 0;
int32_t key[4];
#include "base/android/scoped_hardware_buffer_handle.h"
#endif
+#if defined(TIZEN_TBM_SUPPORT)
+#include "ui/gfx/tbm_buffer_handle.h"
+#endif
+
namespace base {
namespace trace_event {
class ProcessMemoryDump;
#if defined(TIZEN_TBM_SUPPORT)
size_t tbm_surface{0};
size_t media_packet{0};
+ TbmDestroyType tbm_destroy_type = kDestroyTbmByEgl;
int32_t player_id = 0;
int32_t key_num = 0;
int32_t key[4];
copyable_pass_by_value = true
},
{
+ mojom = "gfx.mojom.TbmDestroyType"
+ cpp = "::gfx::TbmDestroyType"
+ },
+ {
mojom = "gfx.mojom.GpuMemoryBufferType"
cpp = "::gfx::GpuMemoryBufferType"
},
int32 id;
};
+// gfx::TbmDestroyType
+[EnableIf=tizen_tbm_support]
+enum TbmDestroyType {
+ kDestroyTbmByEgl = 0,
+ kDestroyTbmByVideoFrame = 1,
+};
+
// gfx::GpuMemoryBufferHandle
struct GpuMemoryBufferHandle {
GpuMemoryBufferId id;
uint64 media_packet;
[EnableIf=tizen_tbm_support]
+ TbmDestroyType tbm_destroy_type;
+
+ [EnableIf=tizen_tbm_support]
int32 player_id;
[EnableIf=tizen_tbm_support]
uint32 media_packet;
[EnableIf=tizen_tbm_support]
+ TbmDestroyType tbm_destroy_type;
+
+ [EnableIf=tizen_tbm_support]
int32 player_id;
[EnableIf=tizen_tbm_support]
out->key_num = data.key_num();
out->width = data.width();
out->height = data.height();
+ data.ReadTbmDestroyType(&out->tbm_destroy_type);
base::span<int32_t> key(out->key);
if (!data.ReadKey(&key))
return false;
out->key_num = data.key_num();
out->width = data.width();
out->height = data.height();
+ data.ReadTbmDestroyType(&out->tbm_destroy_type);
base::span<int32_t> key(out->key);
if (!data.ReadKey(&key))
return false;
static int32_t height(const gfx::GpuMemoryBufferHandle& handle) {
return handle.height;
}
+ static gfx::TbmDestroyType tbm_destroy_type(
+ const gfx::GpuMemoryBufferHandle& handle) {
+ return handle.tbm_destroy_type;
+ }
#endif
static mojo::StructPtr<gfx::mojom::GpuMemoryBufferPlatformHandle>
platform_handle(gfx::GpuMemoryBufferHandle& handle);
static int32_t height(const gfx::TbmBufferHandle& handle) {
return handle.height;
}
-
+ static gfx::TbmDestroyType tbm_destroy_type(
+ const gfx::TbmBufferHandle& handle) {
+ return handle.tbm_destroy_type;
+ }
static bool Read(gfx::mojom::TbmBufferHandleDataView data,
gfx::TbmBufferHandle* handle);
};
+
+template <>
+struct COMPONENT_EXPORT(GFX_SHARED_MOJOM_TRAITS)
+ EnumTraits<gfx::mojom::TbmDestroyType, gfx::TbmDestroyType> {
+ static gfx::mojom::TbmDestroyType ToMojom(gfx::TbmDestroyType type) {
+ switch (type) {
+ case gfx::TbmDestroyType::kDestroyTbmByVideoFrame:
+ return gfx::mojom::TbmDestroyType::kDestroyTbmByVideoFrame;
+ case gfx::TbmDestroyType::kDestroyTbmByEgl:
+ return gfx::mojom::TbmDestroyType::kDestroyTbmByEgl;
+ }
+ NOTREACHED();
+ return gfx::mojom::TbmDestroyType::kDestroyTbmByVideoFrame;
+ }
+
+ static bool FromMojom(gfx::mojom::TbmDestroyType input,
+ gfx::TbmDestroyType* out) {
+ switch (input) {
+ case gfx::mojom::TbmDestroyType::kDestroyTbmByVideoFrame:
+ *out = gfx::TbmDestroyType::kDestroyTbmByVideoFrame;
+ return true;
+ case gfx::mojom::TbmDestroyType::kDestroyTbmByEgl:
+ *out = gfx::TbmDestroyType::kDestroyTbmByEgl;
+ return true;
+ }
+ NOTREACHED();
+ return false;
+ }
+};
#endif
template <>