media_perception: MPS compatibility for M88.

Imports VCS mojom changes and fixes a D-Bus threading crash.

BUG=chromium:1173580,b:179946516
TEST=Verified that the VCS integration works for M88.

Change-Id: Ifc309451c6859525a5d50535ad218a3c0578bf6c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2702238
Tested-by: Luke Sorenson <lasoren@chromium.org>
Commit-Queue: Luke Sorenson <lasoren@chromium.org>
Reviewed-by: Toni Baržić <tbarzic@chromium.org>
(cherry picked from commit aa2bacbf636a1cad2ce030b1dc9a911da054005d)
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/3021667
Reviewed-by: Matthew Ziegelbaum <ziegs@chromium.org>
diff --git a/media_perception/BUILD.gn b/media_perception/BUILD.gn
index 3e3a2fd..eecdb26 100644
--- a/media_perception/BUILD.gn
+++ b/media_perception/BUILD.gn
@@ -50,6 +50,7 @@
     "mojom/devices_changed_observer.mojom",
     "mojom/frame_perception.mojom",
     "mojom/geometry.mojom",
+    "mojom/hdr_metadata.mojom",
     "mojom/hotword_detection.mojom",
     "mojom/image_capture.mojom",
     "mojom/mailbox.mojom",
diff --git a/media_perception/mojo_connector.cc b/media_perception/mojo_connector.cc
index 54b6a10..8dc2387 100644
--- a/media_perception/mojo_connector.cc
+++ b/media_perception/mojo_connector.cc
@@ -361,6 +361,12 @@
   info->descriptor->device_id = video_device.id();
   info->descriptor->display_name = video_device.display_name();
   info->descriptor->capture_api = media::mojom::VideoCaptureApi::VIRTUAL_DEVICE;
+  // No PTZ controls supported.
+  info->descriptor->control_support =
+      media::mojom::VideoCaptureControlSupport::New();
+  info->descriptor->control_support->pan = false;
+  info->descriptor->control_support->tilt = false;
+  info->descriptor->control_support->zoom = false;
   producer_impl->RegisterVirtualDevice(&video_source_provider_,
                                        std::move(info));
 
diff --git a/media_perception/mojom/buffer_types.mojom b/media_perception/mojom/buffer_types.mojom
index 2f3ba36..8a8931e 100644
--- a/media_perception/mojom/buffer_types.mojom
+++ b/media_perception/mojom/buffer_types.mojom
@@ -33,7 +33,9 @@
   CAMERA_AND_CPU_READ_WRITE,
   SCANOUT_CPU_READ_WRITE,
   SCANOUT_VDA_WRITE,
+  PROTECTED_SCANOUT_VDA_WRITE,
   GPU_READ_CPU_READ_WRITE,
+  SCANOUT_VEA_CPU_READ,
   SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE,
 };
 
diff --git a/media_perception/mojom/device_factory.mojom b/media_perception/mojom/device_factory.mojom
index b410a9a..b2d8864 100644
--- a/media_perception/mojom/device_factory.mojom
+++ b/media_perception/mojom/device_factory.mojom
@@ -64,6 +64,12 @@
       media.mojom.VideoCaptureDeviceInfo device_info,
       TextureVirtualDevice& virtual_device);
 
+  // Add a virtual device with video frames backed by GpuMemoryBufferHandle.
+  // The buffers are allocated and managed by the caller.
+  AddGpuMemoryBufferVirtualDevice(
+      media.mojom.VideoCaptureDeviceInfo device_info,
+      pending_receiver<GpuMemoryBufferVirtualDevice> virtual_device_receiver);
+
   // Registered observers will get notified whenever a virtual device is added
   // or removed. Note: Changes to non-virtual devices are currently being
   // monitored outside the video capture service, and therefore the service
diff --git a/media_perception/mojom/hdr_metadata.mojom b/media_perception/mojom/hdr_metadata.mojom
new file mode 100644
index 0000000..ab72968
--- /dev/null
+++ b/media_perception/mojom/hdr_metadata.mojom
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module gfx.mojom;
+
+import "mojom/geometry.mojom";
+
+// This defines a mojo transport format for gfx::HDRMetadata.
+// See ui/gl/hdr_metadata.h for description.
+struct MasteringMetadata {
+    gfx.mojom.PointF primary_r;
+    gfx.mojom.PointF primary_g;
+    gfx.mojom.PointF primary_b;
+    gfx.mojom.PointF white_point;
+    float luminance_max;
+    float luminance_min;
+  };
+
+struct HDRMetadata {
+    MasteringMetadata mastering_metadata;
+    uint32 max_content_light_level;
+    uint32 max_frame_average_light_level;
+  };
diff --git a/media_perception/mojom/media_types.mojom b/media_perception/mojom/media_types.mojom
new file mode 100644
index 0000000..ab5a9ee
--- /dev/null
+++ b/media_perception/mojom/media_types.mojom
@@ -0,0 +1,446 @@
+<<<<<<< HEAD   (c245e7 camera: intel: ipu6: Use the fixed saturation factor for man)
+=======
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module media.mojom;
+
+import "mojom/mailbox_holder.mojom";
+import "mojom/vulkan_ycbcr_info.mojom";
+import "mojom/time.mojom";
+import "mojom/values.mojom";
+import "mojom/unguessable_token.mojom";
+import "mojom/geometry.mojom";
+import "mojom/buffer_types.mojom";
+import "mojom/color_space.mojom";
+import "mojom/hdr_metadata.mojom";
+
+// See media/base/audio_codecs.h for descriptions.
+[Native]
+enum AudioCodec;
+[Native]
+enum AudioCodecProfile;
+
+// See media/base/buffering_state.h for descriptions.
+[Native]
+enum BufferingState;
+[Native]
+enum BufferingStateChangeReason;
+
+// See media/base/channel_layout.h for descriptions.
+[Native]
+enum ChannelLayout;
+
+// See media/base/decode_status.h for descriptions.
+[Native]
+enum DecodeStatus;
+
+// See media/base/status_codes.h for descriptions.
+[Native]
+enum StatusCode;
+
+// See media/base/media_log_record.h for description.
+[Native]
+struct MediaLogRecord;
+
+// See media/base/output_device_info.h for descriptions.
+[Native]
+enum OutputDeviceStatus;
+
+// See media/base/pipeline_status.h for descriptions.
+[Native]
+enum PipelineStatus;
+
+// See media/base/sample_format.h for descriptions.
+[Native]
+enum SampleFormat;
+
+// See media/base/video_codecs.h for descriptions.
+[Native]
+enum VideoCodec;
+
+// See media/base/video_codecs.h for descriptions.
+[Native]
+enum VideoCodecProfile;
+
+// See media/base/video_types.h for descriptions.
+[Native]
+enum VideoPixelFormat;
+
+// See media/base/video_transformation.h for descriptions.
+enum VideoRotation {
+  kVideoRotation0,
+  kVideoRotation90,
+  kVideoRotation180,
+  kVideoRotation270,
+};
+
+// see media/base/video_frame_metadata.h for descriptions.
+enum CopyMode {
+  kCopyToNewTexture,
+  kCopyMailboxesOnly,
+};
+
+// See media/base/video_transformation.h for descriptions.
+struct VideoTransformation {
+  VideoRotation rotation;
+  bool mirrored;
+};
+
+// See media/base/waiting.h for descriptions.
+[Native]
+enum WaitingReason;
+
+// See media/base/watch_time_keys.h for descriptions.
+[Native]
+enum WatchTimeKey;
+
+// See media/base/container_names.h for descriptions.
+[Native]
+enum MediaContainerName;
+
+// See media/base/media_status.h for description.
+[Native]
+enum MediaStatusState;
+
+// This defines a mojo transport format for media::EncryptionPattern
+// See media/base/encryption_pattern.h for description.
+struct EncryptionPattern {
+  uint32 crypt_byte_block;
+  uint32 skip_byte_block;
+};
+
+// See media/base/encryption_scheme.h for description.
+[Native]
+enum EncryptionScheme;
+
+// This defines a mojo transport format for media::VideoColorSpace.
+// See media/base/video_color_space.h for description.
+struct VideoColorSpace {
+  [Native]
+  enum PrimaryID;
+
+  [Native]
+  enum TransferID;
+
+  [Native]
+  enum MatrixID;
+
+  [Native]
+  enum RangeID;
+
+  PrimaryID primaries;
+  TransferID transfer;
+  MatrixID matrix;
+  RangeID range;
+};
+
+
+// This defines a mojo transport format for media::AudioDecoderConfig.
+// See media/base/audio_decoder_config.h for descriptions.
+struct AudioDecoderConfig {
+  AudioCodec codec;
+  AudioCodecProfile profile;
+  SampleFormat sample_format;
+  ChannelLayout channel_layout;
+  int32 samples_per_second;
+  array<uint8> extra_data;
+  mojo_base.mojom.TimeDelta seek_preroll;
+  int32 codec_delay;
+  EncryptionScheme encryption_scheme;
+};
+
+// This defines a mojo transport format for media::VideoDecoderConfig.
+// See media/base/video_decoder_config.h for descriptions.
+struct VideoDecoderConfig {
+  VideoCodec codec;
+  VideoCodecProfile profile;
+  uint32 level;
+  bool has_alpha;
+  VideoTransformation transformation;
+  gfx.mojom.Size coded_size;
+  gfx.mojom.Rect visible_rect;
+  gfx.mojom.Size natural_size;
+  array<uint8> extra_data;
+  EncryptionScheme encryption_scheme;
+  VideoColorSpace color_space_info;
+  gfx.mojom.HDRMetadata? hdr_metadata;
+};
+
+// Native struct media::SubsampleEntry;
+[Native]
+struct SubsampleEntry;
+
+// This defines a mojo transport format for media::DecryptConfig.
+// See media/base/decrypt_config.h for descriptions.
+struct DecryptConfig {
+  EncryptionScheme encryption_scheme;
+  string key_id;
+  string iv;
+  array<SubsampleEntry> subsamples;
+  EncryptionPattern? encryption_pattern;
+};
+
+// This defines a mojo transport format for media::DecoderBuffer.
+struct DecoderBuffer {
+  mojo_base.mojom.TimeDelta timestamp;
+  mojo_base.mojom.TimeDelta duration;
+
+  // Whether the buffer is an end-of-stream (EOS) buffer.
+  bool is_end_of_stream;
+
+  // The number of bytes present in this buffer. The data is not serialized
+  // along with this structure and must be read from a separate DataPipe.
+  // Note that |data_size| could be zero even for a non-EOS buffer (e.g.
+  // with non-empty |size_data|). See http://crbug.com/663438
+  uint32 data_size;
+
+  // Indicates whether or not this buffer is a random access point.
+  bool is_key_frame;
+
+  // Empty when |side_data| doesn't exist.
+  array<uint8> side_data;
+
+  // DecryptConfig for a encrypted buffer. NULL if the buffer is not encrypted.
+  DecryptConfig? decrypt_config;
+
+  // These fields indicate the amount of data to discard after decoding.
+  mojo_base.mojom.TimeDelta front_discard;
+  mojo_base.mojom.TimeDelta back_discard;
+};
+
+// This defines a mojo transport format for media::AudioBuffer.
+struct AudioBuffer {
+  // Format of the audio.
+  SampleFormat sample_format;
+
+  // How the channels are laid out.
+  ChannelLayout channel_layout;
+
+  // Number of channels.
+  int32 channel_count;
+
+  // Sample rate of the buffer.
+  int32 sample_rate;
+
+  // Number of frames in the buffer.
+  int32 frame_count;
+
+  // True if end of stream.
+  bool end_of_stream;
+
+  // Timestamp in microseconds of the first frame.
+  mojo_base.mojom.TimeDelta timestamp;
+
+  // Channel data. Will be empty for EOS buffers.
+  array<uint8> data;
+};
+
+// This defines a mojo transport format for an interleaved, signed
+// 16-bit audio buffer.
+struct AudioDataS16 {
+  // Number of channels.
+  int32 channel_count;
+
+  // Sample rate of the buffer.
+  int32 sample_rate;
+
+  // Number of frames in the buffer.
+  int32 frame_count;
+
+  // Channel data.
+  array<int16> data;
+};
+
+// See media/base/video_frame_metadata.h for a description of fields.
+// TODO(crbug.com/657632): Remove |has_*| values and use nullable types.
+struct VideoFrameMetadata {
+  bool allow_overlay;
+
+  mojo_base.mojom.TimeTicks? capture_begin_time;
+  mojo_base.mojom.TimeTicks? capture_end_time;
+
+  bool has_capture_counter;
+  int32 capture_counter;
+
+  gfx.mojom.Rect? capture_update_rect;
+
+  bool has_copy_mode;
+  CopyMode copy_mode;
+
+  bool end_of_stream;
+
+  mojo_base.mojom.TimeDelta? frame_duration;
+
+  bool has_frame_rate;
+  double frame_rate;
+
+  bool interactive_content;
+
+  mojo_base.mojom.TimeTicks? reference_time;
+
+  bool read_lock_fences_enabled;
+
+  bool has_rotation;
+  VideoRotation rotation;
+
+  bool texture_owner;
+
+  bool wants_promotion_hint;
+
+  bool protected_video;
+
+  bool hw_protected;
+
+  mojo_base.mojom.UnguessableToken? overlay_plane_id;
+
+  bool power_efficient;
+
+  bool has_device_scale_factor;
+  double device_scale_factor;
+
+  bool has_page_scale_factor;
+  double page_scale_factor;
+
+  bool has_root_scroll_offset_x;
+  double root_scroll_offset_x;
+
+  bool has_root_scroll_offset_y;
+  double root_scroll_offset_y;
+
+  bool has_top_controls_visible_height;
+  double top_controls_visible_height;
+
+  mojo_base.mojom.TimeTicks? decode_begin_time;
+  mojo_base.mojom.TimeTicks? decode_end_time;
+
+  mojo_base.mojom.TimeDelta? processing_time;
+
+  bool has_rtp_timestamp;
+  double rtp_timestamp;
+
+  mojo_base.mojom.TimeTicks? receive_time;
+
+  mojo_base.mojom.TimeDelta? wallclock_frame_duration;
+};
+
+// This defines a mojo transport format for media::VideoFrame.
+struct VideoFrame {
+  // Format of the frame.
+  VideoPixelFormat format;
+
+  // Width and height of the video frame, in pixels.
+  gfx.mojom.Size coded_size;
+
+  // Visible size of the frame.
+  gfx.mojom.Rect visible_rect;
+
+  // Natural size of the frame.
+  gfx.mojom.Size natural_size;
+
+  // Timestamp in microseconds of the associated frame.
+  mojo_base.mojom.TimeDelta timestamp;
+
+  // Contents of the video frame (or EOS marker).
+  VideoFrameData data;
+
+  // Extra properties associated with the VideoFrame.
+  VideoFrameMetadata metadata;
+
+  gfx.mojom.ColorSpace color_space;
+  gfx.mojom.HDRMetadata? hdr_metadata;
+};
+
+// Possible choices for storing VideoFrame data.
+union VideoFrameData {
+  EosVideoFrameData eos_data;
+  SharedBufferVideoFrameData shared_buffer_data;
+  DmabufVideoFrameData dmabuf_data;
+  GpuMemoryBufferVideoFrameData gpu_memory_buffer_data;
+  MailboxVideoFrameData mailbox_data;
+};
+
+// A marker for EOS frames.
+struct EosVideoFrameData {
+};
+
+// This defines video frame data stored in a Mojo shared buffer.
+struct SharedBufferVideoFrameData {
+  // Reference to the shared memory containing the frame's data.
+  handle<shared_buffer> frame_data;
+  uint64 frame_data_size;
+
+  // Stride and offsets for each plane. Offsets are relative to the start
+  // of |frame_data|.
+  array<int32> strides;
+  array<uint32> offsets;
+};
+
+// This defines video frame data stored in dmabuf.
+struct DmabufVideoFrameData {
+  // Size depends on media::VideoFrame::NumPlanes with frame format.
+  array<handle<platform>> dmabuf_fds;
+};
+
+struct GpuMemoryBufferVideoFrameData {
+  gfx.mojom.GpuMemoryBufferHandle gpu_memory_buffer_handle;
+  array<gpu.mojom.MailboxHolder, 4> mailbox_holder;
+};
+
+// This defines video frame data stored in texture mailboxes.
+struct MailboxVideoFrameData {
+  // Size must be kept in sync with media::VideoFrame::kMaxPlanes.
+  array<gpu.mojom.MailboxHolder, 4> mailbox_holder;
+  gpu.mojom.VulkanYCbCrInfo? ycbcr_data;
+};
+
+struct PipelineStatistics {
+  uint64 audio_bytes_decoded;
+  uint64 video_bytes_decoded;
+  uint32 video_frames_decoded;
+  uint32 video_frames_dropped;
+  int64 audio_memory_usage;
+  int64 video_memory_usage;
+};
+
+// Set of features for MediaCapabilities prediction.
+// TODO(liberato): consider generalizing this.
+struct PredictionFeatures {
+    // It would be nice to initialize this to VIDEO_CODEC_PROFILE_UNKNOWN (-1),
+    // but we can't do that with native enums.
+    VideoCodecProfile profile;
+    gfx.mojom.Size video_size;
+    // Frames per second may ultimately be a bucketed as an integer, but we want
+    // to do that as late as possible. Using a double avoids early truncation.
+    double frames_per_sec = 0;
+    // Name of the key system used for EME playbacks.
+    string key_system;
+    // Indicates when CDM will use HW secure decoding for EME playbacks.
+    bool use_hw_secure_codecs = false;
+};
+
+// Target values for MediaCapabilities predictions.
+// TODO(liberato): consider generalizing this.
+struct PredictionTargets {
+  uint32 frames_decoded = 0;
+  uint32 frames_dropped = 0;
+  uint32 frames_power_efficient = 0;
+};
+
+// See media/base/pipeline/status.h for descriptions.
+struct PipelineDecoderInfo {
+  string decoder_name;
+  bool is_platform_decoder = false;
+  bool has_decrypting_demuxer_stream = false;
+};
+
+// See media/base/status.h for descriptions.
+struct Status {
+  StatusCode code;
+  string? message;
+  array<mojo_base.mojom.Value> frames;
+  array<media.mojom.Status> causes;
+  mojo_base.mojom.Value? data;
+};
+>>>>>>> CHANGE (aa2bac media_perception: MPS compatibility for M88.)
diff --git a/media_perception/mojom/native_handle_types.mojom b/media_perception/mojom/native_handle_types.mojom
index c0dbf19..03f5630 100644
--- a/media_perception/mojom/native_handle_types.mojom
+++ b/media_perception/mojom/native_handle_types.mojom
@@ -15,7 +15,7 @@
   uint64 size;
 
   // A platform-specific handle the underlying memory object.
-  handle buffer_handle;
+  handle<platform> buffer_handle;
 };
 
 // gfx::NativePixmapHandle
@@ -39,7 +39,7 @@
 struct AHardwareBufferHandle {
   // The actual file descriptor used to wrap the AHardwareBuffer object for
   // serialization.
-  handle buffer_handle;
+  handle<platform> buffer_handle;
 
   // A message pipe handle which tracks the lifetime of this
   // AHardwareBufferHandle. The sender may use this to observe the lifetime
@@ -56,10 +56,10 @@
   NativePixmapHandle native_pixmap_handle;
 
   [EnableIf=is_mac]
-  handle mach_port;
+  handle<platform> mach_port;
 
   [EnableIf=is_win]
-  handle dxgi_handle;
+  handle<platform> dxgi_handle;
 
   [EnableIf=is_android]
   AHardwareBufferHandle android_hardware_buffer_handle;
diff --git a/media_perception/mojom/time.mojom b/media_perception/mojom/time.mojom
index 78cde1a..f49fc18 100644
--- a/media_perception/mojom/time.mojom
+++ b/media_perception/mojom/time.mojom
@@ -10,10 +10,12 @@
   int64 internal_value;
 };
 
+[Stable]
 struct TimeDelta {
   int64 microseconds;
 };
 
+[Stable]
 struct TimeTicks {
   // The internal value is expressed in terms of microseconds since a fixed but
   // intentionally unspecified epoch.
diff --git a/media_perception/mojom/values.mojom b/media_perception/mojom/values.mojom
index ea7c1fa..5ffe845 100644
--- a/media_perception/mojom/values.mojom
+++ b/media_perception/mojom/values.mojom
@@ -8,6 +8,7 @@
 //
 // One notable caveat is that Value supports arbitrary binary data, which JSON
 // does not support natively.
+[Stable]
 union Value {
   // Null type placeholder. This field is never used.
   uint8 null_value;
@@ -29,10 +30,12 @@
 // declaration. Though both of these types are mapped to base::Value in C++,
 // the generated deserialization will guarantee that the method is only invoked
 // with a base::Value of the correct subtype.
+[Stable]
 struct DictionaryValue {
   map<string, Value> storage;
 };
 
+[Stable]
 struct ListValue {
   array<Value> storage;
 };
diff --git a/media_perception/mojom/video_capture_types.mojom b/media_perception/mojom/video_capture_types.mojom
index fecca9c..b03fb60 100644
--- a/media_perception/mojom/video_capture_types.mojom
+++ b/media_perception/mojom/video_capture_types.mojom
@@ -99,6 +99,13 @@
   kGpuMemoryBuffer
 };
 
+// Represents capture device's support for different controls.
+struct VideoCaptureControlSupport {
+  bool pan;
+  bool tilt;
+  bool zoom;
+};
+
 enum VideoCaptureError {
   kNone,
   kVideoCaptureControllerInvalidOrUnsupportedVideoCaptureParametersRequested,
@@ -222,6 +229,7 @@
   kFuchsiaSysmemInvalidBufferSize,
   kFuchsiaUnsupportedPixelFormat,
   kFuchsiaFailedToMapSysmemBuffer,
+  kCrosHalV3DeviceContextDuplicatedClient,
 };
 
 enum VideoCaptureFrameDropReason {
@@ -267,6 +275,27 @@
   bool enable_face_detection;
 };
 
+// |resource_utilization| reports that the frame incurred some fractional
+// utilization of the downstream pipeline's per-frame processing capacity.
+// See code comments in media::base::VideoFrameMetadata for a discussion of
+// how utilization is interpreted. The capturer uses this information to
+// auto-adjust the capture resolution based on performance variances in the
+// live system environment.
+// -1.0 means no value present.
+//
+// |max_framerate_fps| reports requested maximum frame-rate
+// float::inifinty used to signal no limit.
+//
+// |max_pixels| reports requested maximum resolution (in sense of image area).
+// int::max used to signal no limit.
+struct VideoFrameFeedback {
+  double resource_utilization;
+
+  float max_framerate_fps;
+
+  int32 max_pixels;
+};
+
 // Contains one stride value per image plane. Stride means the number of bytes
 // per row. If the image format uses fewer than kMaxPlanes planes, the values
 // for higher plane indices are ignored. For example, for a YUV format, plane
@@ -299,6 +328,10 @@
   string model_id;
   VideoFacingMode facing_mode;
   VideoCaptureApi capture_api;
+<<<<<<< HEAD   (c245e7 camera: intel: ipu6: Use the fixed saturation factor for man)
+=======
+  VideoCaptureControlSupport control_support;
+>>>>>>> CHANGE (aa2bac media_perception: MPS compatibility for M88.)
   VideoCaptureTransportType transport_type;
 };
 
diff --git a/media_perception/mojom/virtual_device.mojom b/media_perception/mojom/virtual_device.mojom
index 57da046..a0186a3 100644
--- a/media_perception/mojom/virtual_device.mojom
+++ b/media_perception/mojom/virtual_device.mojom
@@ -8,6 +8,7 @@
 import "mojom/producer.mojom";
 import "mojom/scoped_access_permission.mojom";
 import "mojom/geometry.mojom";
+import "mojom/buffer_types.mojom";
 
 // Interface for a producer to feed video frames into a virtual
 // device. These frames will appear to the consumer of the device
@@ -66,3 +67,24 @@
   // while the corresponding buffer is still in use via OnFrameReadyInBuffer().
   OnBufferRetired(int32 buffer_id);
 };
+
+// Virtual capture device with video frames backed by GpuMemoryBuffer.
+interface GpuMemoryBufferVirtualDevice {
+  // Registers a GpuMemoryBufferHandle for subsequent transport of frames.
+  OnNewGpuMemoryBufferHandle(
+      int32 buffer_id, gfx.mojom.GpuMemoryBufferHandle gmb_handle);
+
+  // The invoker must guarantee that the GpuMemoryBufferHandle with |buffer_id|
+  // stay valid until |access_permission| is released by the invocation target.
+  // In |frame_info|, |visible_rect| must be equivalent to the full |coded_size|
+  // of the frame, i.e. using |visible_rect| to crop to subregions of the frame
+  // is not supported.
+  OnFrameReadyInBuffer(int32 buffer_id,
+                       pending_remote<ScopedAccessPermission> access_permission,
+                       media.mojom.VideoFrameInfo frame_info);
+
+  // Unregisters a GpuMemoryBufferHandle previously registered via
+  // OnNewGpuMemoryBufferHandle(). Note, that this should not be called while
+  // the corresponding buffer is still in use via OnFrameReadyInBuffer().
+  OnBufferRetired(int32 buffer_id);
+};
diff --git a/media_perception/output_manager.cc b/media_perception/output_manager.cc
index 00373b8..9c789fa 100644
--- a/media_perception/output_manager.cc
+++ b/media_perception/output_manager.cc
@@ -26,12 +26,30 @@
 
 }  // namespace
 
+OutputManager::~OutputManager() {
+  if (thread_.IsRunning()) {
+    base::WaitableEvent destruction_complete(
+        base::WaitableEvent::ResetPolicy::MANUAL,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&OutputManager::DestructDbus, base::Unretained(this),
+                       &destruction_complete));
+    destruction_complete.Wait();
+  }
+}
+
+void OutputManager::DestructDbus(base::WaitableEvent* destruction_complete) {
+  dbus_connection_.reset();
+  destruction_complete->Signal();
+}
+
 OutputManager::OutputManager(
     const std::string& configuration_name,
     std::shared_ptr<Rtanalytics> rtanalytics,
     const PerceptionInterfaces& interfaces,
-    chromeos::media_perception::mojom::PerceptionInterfacesPtr*
-        interfaces_ptr) {
+    chromeos::media_perception::mojom::PerceptionInterfacesPtr* interfaces_ptr)
+    : thread_("OutputManager Dbus Thread") {
   // Save the configuration name in case we need to reference it later.
   configuration_name_ = configuration_name;
   rtanalytics_ = rtanalytics;
@@ -257,14 +275,16 @@
                        << output.stream_name();
             continue;
           }
-          // Setup D-Bus connection.
-          bus_ = dbus_connection_.Connect();
-          if (bus_ == nullptr) {
-            LOG(FATAL) << "Unable to connect to Dbus from OutputManager.";
+
+          if (!thread_.StartWithOptions(
+                  base::Thread::Options(base::MessagePumpType::IO, 0))) {
+            LOG(ERROR) << "Failed to create dbus thread.";
+            continue;
           }
-          dbus_proxy_ = bus_->GetObjectProxy(
-              "org.chromium.IpPeripheralService",
-              dbus::ObjectPath("/org/chromium/IpPeripheralService"));
+          dbus_connection_ = std::make_unique<brillo::DBusConnection>();
+          thread_.task_runner()->PostTask(
+              FROM_HERE, base::Bind(&OutputManager::InitializeDbus,
+                                    base::Unretained(this)));
         }
       }
       continue;
@@ -272,6 +292,16 @@
   }
 }
 
+void OutputManager::InitializeDbus() {
+  bus_ = dbus_connection_->Connect();
+  if (bus_ == nullptr) {
+    LOG(FATAL) << "Unable to connect to Dbus from OutputManager.";
+  }
+  dbus_proxy_ = bus_->GetObjectProxy(
+      "org.chromium.IpPeripheralService",
+      dbus::ObjectPath("/org/chromium/IpPeripheralService"));
+}
+
 void OutputManager::HandleFramePerception(const std::vector<uint8_t>& bytes) {
   if (!frame_perception_handler_ptr_.is_bound()) {
     LOG(WARNING) << "Got frame perception output but handler ptr is not bound.";
@@ -372,6 +402,15 @@
 
 void OutputManager::HandleIndexedTransitions(
     const std::vector<uint8_t>& bytes) {
+  thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&OutputManager::HandleIndexedTransitionsOnDbusThread,
+                     base::Unretained(this), bytes));
+}
+
+void OutputManager::HandleIndexedTransitionsOnDbusThread(
+    const std::vector<uint8_t>& bytes) {
+  DCHECK(thread_.task_runner()->BelongsToCurrentThread());
   std::string falcon_ip = rtanalytics_->GetFalconIp(configuration_name_);
   std::size_t found = falcon_ip.find_last_of(".");
   if (found == -1) {
diff --git a/media_perception/output_manager.h b/media_perception/output_manager.h
index fc3ee29..c5ad5be 100644
--- a/media_perception/output_manager.h
+++ b/media_perception/output_manager.h
@@ -11,6 +11,10 @@
 #include <brillo/dbus/dbus_connection.h>
 #include <dbus/message.h>
 
+#include <base/bind.h>
+#include <base/single_thread_task_runner.h>
+#include <base/synchronization/waitable_event.h>
+#include <base/threading/thread.h>
 #include "base/memory/weak_ptr.h"
 #include "media_perception/media_perception_mojom.pb.h"
 #include "media_perception/perception_interface.pb.h"
@@ -24,7 +28,7 @@
 // output types for a particular pipeline are.
 class OutputManager {
  public:
-  OutputManager() {}
+  OutputManager() : thread_("OutputManager Dbus Thread") {}
 
   OutputManager(const std::string& configuration_name,
                 std::shared_ptr<Rtanalytics> rtanalytics,
@@ -32,6 +36,8 @@
                 chromeos::media_perception::mojom::PerceptionInterfacesPtr*
                     interfaces_ptr);
 
+  ~OutputManager();
+
   void HandleFramePerception(const std::vector<uint8_t>& bytes);
 
   void HandleHotwordDetection(const std::vector<uint8_t>& bytes);
@@ -50,12 +56,19 @@
  private:
   void HandleFalconPtzTransitionResponse(dbus::Response* response);
 
+  void InitializeDbus();
+
+  void HandleIndexedTransitionsOnDbusThread(const std::vector<uint8_t>& bytes);
+
+  void DestructDbus(base::WaitableEvent* destruction_complete);
+
   std::string configuration_name_;
 
   std::shared_ptr<Rtanalytics> rtanalytics_;
 
   // D-Bus objects for sending messages to the Falcon camera.
-  brillo::DBusConnection dbus_connection_;
+  base::Thread thread_;
+  std::unique_ptr<brillo::DBusConnection> dbus_connection_;
   scoped_refptr<::dbus::Bus> bus_;
   dbus::ObjectProxy* dbus_proxy_;